├── .WolframResources ├── .gitignore ├── .project ├── CHANGELOG.md ├── CMakeLists.txt ├── CONTRIBUTING.md ├── CodeParser └── Data │ └── LongNames.wl ├── CodeTools └── Generate │ ├── CreatePacletArchive.wl │ └── GenerateSources.wl ├── HowToBuild.md ├── LICENSE ├── LSPServer ├── Documentation │ └── English │ │ ├── Guides │ │ └── LSPServer.nb │ │ └── ReferencePages │ │ └── Symbols │ │ └── StartServer.nb ├── Generate │ └── ReplaceLongNamePUA.wl ├── Kernel │ ├── BracketMismatches.wl │ ├── CodeAction.wl │ ├── Color.wl │ ├── Definitions.wl │ ├── Diagnostics.wl │ ├── DocumentSymbol.wl │ ├── FoldingRange.wl │ ├── Formatting.wl │ ├── Hover.wl │ ├── ImplicitTokens.wl │ ├── LSPServer.wl │ ├── Library.wl │ ├── ListenSocket.wl │ ├── References.wl │ ├── SelectionRange.wl │ ├── SemanticTokens.wl │ ├── ServerDiagnostics.wl │ ├── Socket.wl │ ├── StdIO.wl │ ├── Utils.wl │ └── Workspace.wl └── PacletInfo.wl.in ├── README.md ├── Tests ├── LSPServerNotes.nb ├── ScopingExamples.wl ├── ServerDiagnostics.mt ├── TestRunner.nb ├── TestSuite.mt ├── hover │ ├── Delayed.wlt │ ├── DelayedTest.wl │ ├── InFileUsage.wlt │ ├── InFileUsageTest.wl │ ├── Init.wl │ ├── MissingCases.wlt │ ├── MissingCasesTest.wl │ ├── Package.wlt │ ├── PackageTest.wl │ ├── SystemSymbol.wlt │ ├── SystemSymbolTest.wl │ ├── Values.wlt │ └── ValuesTest.wl └── outline │ └── test.wl ├── WolframLanguageSyntax └── Data │ ├── AnalyzableMessagePositions.wl │ ├── AnalyzableMessages.wl │ ├── BadSymbols.wl │ ├── BuiltinFunctions.wl │ ├── Constants.wl │ ├── ExperimentalSymbols.wl │ ├── FreeCharacters.wl │ ├── FreeLongNames.wl │ ├── ObsoleteSymbols.wl │ ├── Options.wl │ ├── SessionSymbols.wl │ ├── SpecialCharacters.wl │ ├── SpecialLongNames.wl │ ├── SystemCharacters.wl │ ├── SystemLongNames.wl │ ├── UndocumentedCharacters.wl │ ├── UndocumentedLongNames.wl │ ├── UndocumentedSymbols.wl │ ├── UnsupportedCharacters.wl │ └── UnsupportedLongNames.wl ├── cmake ├── InspectFile.cmake ├── InstallPaclet.cmake ├── MacOSXVersionMin.cmake ├── PacletInfo.cmake ├── ReplacePacletInfo.cmake ├── WolframKernel.cmake ├── WolframLibrary.cmake └── WolframScript.cmake ├── cpp ├── include │ └── LSPServer.h └── src │ └── LSPServer.cpp ├── docs ├── IDE-Tests.nb ├── compatibility.md ├── diagnostics.md ├── notes.md └── sockets.md └── scripts └── re_build_LSPServer.xml /.WolframResources: -------------------------------------------------------------------------------- 1 | Resources[ 2 | Version[1], 3 | ExecutionBuildCommand["< 2 | 3 | LSPServer 4 | 5 | 6 | 7 | 8 | 9 | com.wolfram.eclipse.MEET.MathematicaProjectBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.wolfram.eclipse.MEET.SimpleMathematicaNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## 1.9 - XX Dec, 2022 3 | 4 | 5 | ## 1.8 - 10 Oct, 2022 6 | 7 | Get rid of build date checking. 8 | 9 | Do not kill the kernel if URINotFound is thrown. 10 | 11 | ### Fixes 12 | 13 | Fix bad formatting of kernel version. 14 | 15 | 16 | ## 1.7 - 4 July, 2022 17 | 18 | Fix handling non-BMP PUA characters 19 | 20 | Address issues here: https://github.com/WolframResearch/vscode-wolfram/issues/10 21 | 22 | Only compare major.minor when doing version checks and only do build 23 | date check if versions are identical 24 | 25 | Add foldingRange 26 | 27 | 13.1 syntax updates 28 | 29 | 30 | ## 1.6 - 12 May, 2022 31 | 32 | Add function usage and function definition patterns to hover. 33 | 34 | 35 | ### Fixes 36 | 37 | Fix handling stale content for textDocument/documentSymbol and textDocument/references 38 | 39 | 40 | ## 1.5 - 7 Mar, 2022 41 | 42 | Add ProcessDirectory option to RunServerDiagnostic 43 | 44 | Add Hierarchical Document Symbol support (outlines) 45 | 46 | Add a mini server to diagnostics to test for various bugs before doing actual diagnostics 47 | 48 | Work-around serious bug 419428 for now 49 | 50 | Internal\`WithLocalSettings is broken, so manually insert UnlockQueue[] calls 51 | 52 | Use 0.4 seconds, same as default value of spelling squiggly in FE 53 | 54 | Handle ScheduledTask in earlier versions before it held its expr 55 | 56 | FromDateString was introduced in 12.3, so use a version check 57 | 58 | https://github.com/WolframResearch/vscode-wolfram/issues/8 59 | 60 | Create a special message for the error and put in queue as regular traffic 61 | 62 | This guarantees the proper order of things being read 63 | 64 | initializationOptions may be Null 65 | 66 | Handle workspace/didChangeConfiguration notification 67 | 68 | https://github.com/WolframResearch/LSPServer/issues/1 69 | 70 | 13.0.1 syntax updates 71 | 72 | Only try reporting stdout / stderr for up to 1 second 73 | 74 | 75 | ### Fixes 76 | 77 | Fix race condition with stdio error being checked before all previous traffic has been processed 78 | 79 | Fix issues found by running with Jupyter Lab LSP 80 | 81 | 82 | ## 1.4 - 25 Oct, 2021 83 | 84 | Add Startup Message handling 85 | 86 | There may be internal errors in LSPServer that emit messages during ``Needs["LSPServer`"] `` 87 | 88 | These messages are exceptionally hard to handle because any code for handling has not yet been loaded 89 | 90 | The messages may cause unexplained hangs in clients 91 | 92 | So manually set $Messages to a tmp file and then handle the messages later 93 | 94 | 95 | Do not allow PacletManager to participate in finding \`Generate\` files 96 | 97 | 98 | Add more features to RunServerDiagnostic: 99 | 100 | Print given kernel path and kernel path to-be-started, and check they are the same. 101 | 102 | Add a 30 second timeout for the while diagnostic. 103 | 104 | Keep track of how long initialize takes, and error if greater than 10 seconds 105 | 106 | 107 | Use Internal\`WithLocalSettings to protect against aborts when doing LockQueue / UnlockQueue 108 | 109 | 110 | RunServerDiagnostic: reduce "must be run with same kernel" to warning 111 | 112 | 113 | If there were messages when loading LSPServer\`, then report in the diagnostic 114 | 115 | 116 | use FromDateString with `"Language" -> "en"` for more robust date parsing 117 | 118 | 119 | ### Fixes 120 | 121 | Various fixes for RunServerDiagnostic: 122 | 123 | BinaryWrite may fail, so check return value and quiet `BinaryWrite::errfile` 124 | 125 | If `arr == {}` returns unevaluated, then whole Which returns unevaluated 126 | 127 | 128 | ## 1.3 - 30 Aug, 2021 129 | 130 | Notes on compatibility have been added to docs/compatibility.md 131 | 132 | work around bug 410895, all quotes are stripped from StartProcess on Windows 133 | 134 | Experimental support for sockets 135 | 136 | Experimental support for multiple clients 137 | 138 | 139 | ## 1.2 - 25 Mar, 2021 140 | 141 | Allow `textDocument/definition` to lookup symbols with or without contexts 142 | 143 | e.g., allow foo\`bar to look up definition for bar and vice versa 144 | 145 | Allow `textDocument/hover` to work with symbols and display their usage messages. 146 | 147 | The usage messages are parsed directly from the linear syntax. 148 | 149 | Add a build step to generate a file ReplacePUA.wl that provides a map for converting PUA characters to ASCII approximations. 150 | 151 | Use LongNames.wl to provide a text replacement for PUA characters that cannot render properly in other editors 152 | 153 | Add a background thread for reading from stdin. This thread will write to a queue that the server will read on its main thread. 154 | 155 | The server will look at the queue and determine if any of the messages can be discarded. 156 | 157 | For example, a long sequence of `textDocument/didChange` requests do not need to be processed. Only the final one needs to be processed. 158 | 159 | Similarly, other requests that may be in the queue before a `textDocument/didChange` may also be discarded. 160 | 161 | Do a little work on only reparsing if needed. 162 | 163 | Handle `textDocument/documentSymbol` 164 | 165 | Handle more color literals and also handle `textDocument/colorPresentation` 166 | 167 | LSP clients will have a 10 second timeout for starting the kernel. After that, a dialog is presented explaining that there is a problem and diagnostic code is presented to run in a notebook. 168 | 169 | Introduce delays for running various methods. 170 | 171 | Implementation of `textDocument/selectionRange` 172 | 173 | Initial implementation of semantic tokens 174 | 175 | 176 | ## 1.1 - 16 Sep, 2020 177 | 178 | Initial work with formatting 179 | 180 | Wire up tabSize and insertSpaces options 181 | 182 | Add range formatting 183 | 184 | Introduce new BracketMatcher UI 185 | 186 | Handle workspace/didChangeWachedFiles that comes from IntelliJ plugin 187 | 188 | Add implicit tokens for ExpectedOperands 189 | 190 | 191 | ## 1.0 - 2 Apr, 2020 192 | 193 | Add preliminary implementation of hover 194 | 195 | Add a native library for handling stdio, and remove the Python proxy script 196 | 197 | Keep all content as ByteArrays and introduce publishing of implicit Times 198 | 199 | Add versions notification 200 | 201 | Add a definitions provider 202 | 203 | Display other implicit tokens 204 | 205 | 206 | Enable sending implicit 1, implicit All, and implicit Null to a client. 207 | A little language has been invented for representing implicit tokens, 208 | and combinations thereof, with a single character. 209 | 210 | 211 | ## 0.15 - 15 Jan, 2020 212 | 213 | Add Creator field 214 | 215 | Quit kernel if any messages on startup 216 | 217 | Add color provider 218 | 219 | Require using `File[]` wrapper 220 | 221 | 222 | ## 0.14 - 28 Oct, 2019 223 | 224 | Add support for CodeActions 225 | 226 | Add ConfidenceLevel setting 227 | 228 | Handle other CodeAction commands 229 | 230 | Remove Lints that are shadowed 231 | 232 | Only convert bytes to string if debug logging 233 | 234 | 235 | ### Fixes 236 | 237 | Handle `$/` messages gracefully 238 | 239 | Fix handling of non-ASCII characters 240 | 241 | 242 | ## 0.13 - 16 Sep, 2019 243 | 244 | Use `"AdditionalSources"` for Lints 245 | 246 | 247 | ## 0.12 - 5 Aug, 2019 248 | 249 | Add some error handling for missing files and directories. 250 | 251 | Add --extra argument for extra arguments to WolframKernel 252 | 253 | Various bug fixes. 254 | 255 | 256 | ## 0.11 - 10 Jun, 2019 257 | 258 | Added LSPServer paclet to CodeTools suite. 259 | 260 | Various bug fixes. 261 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Wolfram® 2 | 3 | Thank you for taking the time to contribute to the [Wolfram Research](https://github.com/wolframresearch) repos on GitHub. 4 | 5 | ## Licensing of Contributions 6 | 7 | By contributing to Wolfram, you agree and affirm that: 8 | 9 | > Wolfram may release your contribution under the terms of the [MIT license](https://opensource.org/licenses/MIT); and 10 | 11 | > You have read and agreed to the [Developer Certificate of Origin](http://developercertificate.org/), version 1.1 or later. 12 | 13 | Please see [LICENSE](LICENSE) for licensing conditions pertaining 14 | to individual repositories. 15 | 16 | 17 | ## Bug reports 18 | 19 | ### Security Bugs 20 | 21 | Please **DO NOT** file a public issue regarding a security issue. 22 | Rather, send your report privately to security@wolfram.com. Security 23 | reports are appreciated and we will credit you for it. We do not offer 24 | a security bounty, but the forecast in your neighborhood will be cloudy 25 | with a chance of Wolfram schwag! 26 | 27 | ### General Bugs 28 | 29 | Please use the repository issues page to submit general bug issues. 30 | 31 | Please do not duplicate issues. 32 | 33 | Please do send a complete and well-written report to us. Note: **the 34 | thoroughness of your report will positively correlate to our willingness 35 | and ability to address it**. 36 | 37 | When reporting issues, always include: 38 | 39 | * Your version of *Mathematica*® or the Wolfram Language. 40 | * Your operating system. 41 | -------------------------------------------------------------------------------- /CodeTools/Generate/CreatePacletArchive.wl: -------------------------------------------------------------------------------- 1 | 2 | If[!MemberQ[$Path, #], PrependTo[$Path, #]]&[DirectoryName[$InputFileName, 3]] 3 | 4 | BeginPackage["CodeTools`Generate`CreatePacletArchive`"] 5 | 6 | Begin["`Private`"] 7 | 8 | (* 9 | Do not allow PacletManager to participate in finding `Generate` files 10 | 11 | PacletManager will find e.g. CodeParser/Kernel/TokenEnum.wl when asked to find CodeParser`Generate`TokenEnum` 12 | 13 | related issues: PACMAN-54 14 | *) 15 | Block[{Internal`PacletFindFile = Null&}, 16 | Needs["CodeTools`Generate`GenerateSources`"]; 17 | ] 18 | If[$VersionNumber < 12.1, 19 | Needs["PacletManager`"] 20 | ] 21 | 22 | checkBuildDir[] 23 | checkPaclet[] 24 | checkPacletLayoutDir[] 25 | 26 | 27 | If[retry, 28 | (* 29 | CreatePacletArchive may be slow on RE machines, so allow re-trying if JLink connection timeout is hit 30 | 31 | Set $connectTimeout to some large value and cross fingers (default is 20) 32 | 33 | See: RE-515885 34 | *) 35 | Needs["JLink`"]; 36 | JLink`InstallJava`Private`$connectTimeout = 300.0 37 | ] 38 | 39 | 40 | generate[] := ( 41 | 42 | Print["Calling CreatePacletArchive..."]; 43 | 44 | If[$VersionNumber >= 12.1, 45 | res = System`CreatePacletArchive[FileNameJoin[{pacletLayoutDir, paclet}], FileNameJoin[{buildDir, "paclet"}]] 46 | , 47 | res = PacletManager`PackPaclet[FileNameJoin[{pacletLayoutDir, paclet}], FileNameJoin[{buildDir, "paclet"}]] 48 | ]; 49 | 50 | Print[res]; 51 | 52 | If[!StringQ[res], 53 | Quit[1] 54 | ]; 55 | 56 | Print["Done CreatePacletArchive"] 57 | ) 58 | 59 | If[!StringQ[script], 60 | Quit[1] 61 | ] 62 | If[AbsoluteFileName[script] === AbsoluteFileName[$InputFileName], 63 | generate[] 64 | ] 65 | 66 | End[] 67 | 68 | EndPackage[] 69 | -------------------------------------------------------------------------------- /CodeTools/Generate/GenerateSources.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["CodeTools`Generate`GenerateSources`"] 2 | 3 | buildDirFlagPosition 4 | 5 | buildDir 6 | 7 | srcDirFlagPosition 8 | 9 | srcDir 10 | 11 | script 12 | 13 | pacletFlagPosition 14 | 15 | paclet 16 | 17 | retryFlagPosition 18 | 19 | retry 20 | 21 | pacletLayoutDirFlagPosition 22 | 23 | pacletLayoutDir 24 | 25 | 26 | checkBuildDir 27 | 28 | checkSrcDir 29 | 30 | checkPaclet 31 | 32 | checkPacletLayoutDir 33 | 34 | 35 | Begin["`Private`"] 36 | 37 | buildDirFlagPosition = FirstPosition[$CommandLine, "-buildDir"] 38 | 39 | buildDir := buildDir = $CommandLine[[buildDirFlagPosition[[1]] + 1]] 40 | 41 | srcDirFlagPosition = FirstPosition[$CommandLine, "-srcDir"] 42 | 43 | srcDir := srcDir = $CommandLine[[srcDirFlagPosition[[1]] + 1]] 44 | 45 | scriptPosition = FirstPosition[$CommandLine, "-script"] 46 | 47 | script := script = $CommandLine[[scriptPosition[[1]] + 1]] 48 | 49 | pacletFlagPosition = FirstPosition[$CommandLine, "-paclet"] 50 | 51 | paclet := paclet = $CommandLine[[pacletFlagPosition[[1]] + 1]] 52 | 53 | retryFlagPosition = FirstPosition[$CommandLine, "-retry"] 54 | 55 | retry = !MissingQ[retryFlagPosition] 56 | 57 | pacletLayoutDirFlagPosition = FirstPosition[$CommandLine, "-pacletLayoutDir"] 58 | 59 | pacletLayoutDir := pacletLayoutDir = $CommandLine[[pacletLayoutDirFlagPosition[[1]] + 1]] 60 | 61 | 62 | checkBuildDir[] := 63 | Module[{}, 64 | If[MissingQ[buildDirFlagPosition], 65 | Print["Cannot proceed; buildDir flag missing"]; 66 | Quit[1] 67 | ]; 68 | 69 | If[!DirectoryQ[buildDir], 70 | Print["Cannot proceed; Unsupported buildDir: ", buildDir]; 71 | Quit[1] 72 | ]; 73 | ] 74 | 75 | 76 | checkSrcDir[] := 77 | Module[{}, 78 | If[MissingQ[srcDirFlagPosition], 79 | Print["Cannot proceed; srcDir flag missing"]; 80 | Quit[1] 81 | ]; 82 | 83 | If[!DirectoryQ[srcDir], 84 | Print["Cannot proceed; Unsupported srcDir: ", srcDir]; 85 | Quit[1] 86 | ]; 87 | ] 88 | 89 | 90 | checkPaclet[] := 91 | Module[{}, 92 | If[MissingQ[pacletFlagPosition], 93 | Print["Cannot proceed; paclet flag missing"]; 94 | Quit[1] 95 | ]; 96 | ] 97 | 98 | 99 | checkPacletLayoutDir[] := 100 | Module[{}, 101 | If[MissingQ[pacletLayoutDirFlagPosition], 102 | Print["Cannot proceed; pacletLayoutDir flag missing"]; 103 | Quit[1] 104 | ]; 105 | 106 | If[!DirectoryQ[pacletLayoutDir], 107 | Print["Cannot proceed; Unsupported pacletLayoutDir: ", pacletLayoutDir]; 108 | Quit[1] 109 | ]; 110 | 111 | If[FileNameTake[pacletLayoutDir, -1] =!= "paclet", 112 | Print["Cannot proceed; Unsupported pacletLayoutDir: ", pacletLayoutDir]; 113 | Quit[1] 114 | ]; 115 | ] 116 | 117 | End[] 118 | 119 | EndPackage[] 120 | -------------------------------------------------------------------------------- /HowToBuild.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | LSPServer uses a Wolfram Language kernel to generate code at build time and a C++ compiler to compile a native library. 4 | 5 | LSPServer uses C++11 features and requires a compiler that can support at least C++11. 6 | 7 | LSPServer uses CMake to generate build scripts. 8 | 9 | Here is an example transcript using the default make generator to build LSPServer: 10 | ``` 11 | cd lspserver 12 | mkdir build 13 | cd build 14 | cmake .. 15 | cmake --build . 16 | ``` 17 | 18 | The result is a directory named `paclet` that contains the WL package source code and a built LSPServer `.paclet` file for installing. 19 | 20 | Inside a kernel session you may then install the paclet by evaluating: 21 | ``` 22 | PacletInstall["/path/to/build/paclet/LSPServer-1.9.paclet"] 23 | ``` 24 | 25 | Specify `MATHEMATICA_INSTALL_DIR` if you have Wolfram System installed in a non-default location: 26 | ``` 27 | cmake -DMATHEMATICA_INSTALL_DIR=/Applications/Mathematica.app/Contents/ .. 28 | cmake --build . 29 | ``` 30 | 31 | On Windows: 32 | ``` 33 | cmake -DMATHEMATICA_INSTALL_DIR="C:/Program Files/Wolfram Research/Mathematica/13.1" .. 34 | cmake --build . 35 | ``` 36 | 37 | ## Installing 38 | 39 | You can install the paclet from CMake: 40 | ``` 41 | cmake --install . 42 | ``` 43 | 44 | This starts a kernel and calls `PacletInstall` with the built .paclet file. 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Wolfram Research Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LSPServer/Generate/ReplaceLongNamePUA.wl: -------------------------------------------------------------------------------- 1 | (* ::Package::"Tags"-><|"SuspiciousSessionSymbol" -> <|Enabled -> False|>|>:: *) 2 | 3 | If[!MemberQ[$Path, #], PrependTo[$Path, #]]&[DirectoryName[$InputFileName, 3]] 4 | 5 | BeginPackage["LSPServer`Generate`ReplaceLongNamePUA`"] 6 | 7 | 8 | Begin["`Private`"] 9 | 10 | (* 11 | Do not allow PacletManager to participate in finding `Generate` files 12 | 13 | PacletManager will find e.g. CodeParser/Kernel/TokenEnum.wl when asked to find CodeParser`Generate`TokenEnum` 14 | 15 | related issues: PACMAN-54 16 | *) 17 | Block[{Internal`PacletFindFile = Null&}, 18 | Needs["CodeTools`Generate`GenerateSources`"]; 19 | ] 20 | 21 | 22 | checkBuildDir[] 23 | checkSrcDir[] 24 | 25 | 26 | dataDir = FileNameJoin[{srcDir, "CodeParser", "Data"}] 27 | 28 | importedLongNames = Get[FileNameJoin[{dataDir, "LongNames.wl"}]] 29 | 30 | 31 | longNamePUAChars = Select[importedLongNames, ((16^^e000 <= #[[2]] <= 16^^f8ff) || (16^^0f0000 <= #[[2]] <= 16^^0ffffd) || (16^^100000 <= #[[2]] <= 16^^10fffd))&] 32 | 33 | replacements = Select[longNamePUAChars, MatchQ[#[[3]], KeyValuePattern["ASCIIReplacements" -> _]]&] 34 | 35 | nonReplacements = Select[longNamePUAChars, (!MatchQ[#[[3]], KeyValuePattern["ASCIIReplacements" -> _]] && !MemberQ[{"RawCharacter", "UnsupportedCharacter"}, SymbolName[#[[1]]]])&] 36 | 37 | 38 | 39 | 40 | 41 | Attributes[HoldFormBlindToInputForm] = {HoldFirst} 42 | 43 | Format[HoldFormBlindToInputForm[s_Symbol], InputForm] := s 44 | 45 | Format[HoldFormBlindToInputForm[s_String], InputForm] := s 46 | 47 | Format[HoldFormBlindToInputForm[h_[arg1_]], InputForm] := 48 | HoldFormBlindToInputForm[h][HoldFormBlindToInputForm[arg1]] 49 | 50 | 51 | (* 52 | Basically just converts "Alpha" => "\[Alpha]" 53 | 54 | But handles newly-added characters by converting to \: notation 55 | 56 | TODO: I would just write out \:xxxx if it were easy to do... 57 | *) 58 | toChar[k_, v_] := 59 | Module[{}, 60 | If[MatchQ[v[[3]], KeyValuePattern["Added" -> _]], 61 | With[{str = "\"" <> If[v[[2]] > 16^^FFFF, "\\|" <> IntegerString[v[[2]], 16, 6], "\\:" <> IntegerString[v[[2]], 16, 4]] <> "\""}, 62 | HoldFormBlindToInputForm[ToExpression[str]] 63 | ] 64 | , 65 | ToExpression["\"\\["<> k <> "]\""] 66 | ] 67 | ] 68 | 69 | 70 | 71 | replaceLongNamePUARules = 72 | KeyValueMap[ 73 | Function[{k, v}, 74 | toChar[k, v] -> v[[3, Key["ASCIIReplacements"], -1]] 75 | ] 76 | , 77 | replacements 78 | ] ~Join~ 79 | 80 | KeyValueMap[ 81 | Function[{k, v}, 82 | toChar[k, v] -> (" " <> k <> " ") 83 | ] 84 | , 85 | nonReplacements 86 | ] 87 | 88 | 89 | generate[] := ( 90 | 91 | Print["Generating ReplaceLongNamePUA..."]; 92 | 93 | replaceLongNamePUAWL = { 94 | " 95 | (* 96 | AUTO GENERATED FILE 97 | DO NOT MODIFY 98 | *) 99 | 100 | BeginPackage[\"LSPServer`ReplaceLongNamePUA`\"] 101 | 102 | replaceLongNamePUA 103 | 104 | Begin[\"`Private`\"] 105 | 106 | replaceLongNamePUA[s_String] := 107 | StringReplace[s, "} ~Join~ 108 | 109 | {ToString[replaceLongNamePUARules, InputForm, CharacterEncoding -> "ASCII", PageWidth -> 120]} ~Join~ 110 | {" ] 111 | 112 | End[] 113 | 114 | EndPackage[] 115 | " 116 | }; 117 | 118 | Print["exporting ReplaceLongNamePUA.wl"]; 119 | res = Export[FileNameJoin[{buildDir, "paclet", "LSPServer", "Kernel", "ReplaceLongNamePUA.wl"}], Column[replaceLongNamePUAWL], "String"]; 120 | 121 | If[FailureQ[res], 122 | Print[res]; 123 | Quit[1] 124 | ]; 125 | 126 | Print["Done ReplaceLongNamePUA"]; 127 | ) 128 | 129 | If[!StringQ[script], 130 | Quit[1] 131 | ] 132 | If[AbsoluteFileName[script] === AbsoluteFileName[$InputFileName], 133 | generate[] 134 | ] 135 | 136 | End[] 137 | 138 | EndPackage[] 139 | -------------------------------------------------------------------------------- /LSPServer/Kernel/CodeAction.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`CodeAction`"] 2 | 3 | Begin["`Private`"] 4 | 5 | Needs["LSPServer`"] 6 | Needs["LSPServer`Utils`"] 7 | Needs["CodeInspector`"] 8 | Needs["CodeInspector`Utils`"] 9 | Needs["CodeParser`"] 10 | Needs["CodeParser`Utils`"] 11 | 12 | 13 | expandContent[content:KeyValuePattern["method" -> "textDocument/codeAction"], pos_] := 14 | Catch[ 15 | Module[{params, id, doc, uri}, 16 | 17 | If[$Debug2, 18 | log["textDocument/codeAction: enter expand"] 19 | ]; 20 | 21 | id = content["id"]; 22 | params = content["params"]; 23 | 24 | If[Lookup[$CancelMap, id, False], 25 | 26 | $CancelMap[id] =.; 27 | 28 | If[$Debug2, 29 | log["canceled"] 30 | ]; 31 | 32 | Throw[{<| "method" -> "textDocument/codeActionFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 33 | ]; 34 | 35 | doc = params["textDocument"]; 36 | uri = doc["uri"]; 37 | 38 | If[isStale[$PreExpandContentQueue[[pos[[1]]+1;;]], uri], 39 | 40 | If[$Debug2, 41 | log["stale"] 42 | ]; 43 | 44 | Throw[{<| "method" -> "textDocument/codeActionFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 45 | ]; 46 | 47 | <| "method" -> #, "id" -> id, "params" -> params |>& /@ { 48 | "textDocument/concreteParse", 49 | "textDocument/suppressedRegions", 50 | "textDocument/runConcreteDiagnostics", 51 | "textDocument/aggregateParse", 52 | "textDocument/runAggregateDiagnostics", 53 | "textDocument/abstractParse", 54 | "textDocument/runAbstractDiagnostics", 55 | "textDocument/codeActionFencepost" 56 | } 57 | ]] 58 | 59 | handleContent[content:KeyValuePattern["method" -> "textDocument/codeActionFencepost"]] := 60 | Catch[ 61 | Module[{id, params, doc, uri, actions, range, lints, lspAction, lspActions, edit, diagnostics, 62 | command, label, actionData, actionSrc, replacementNode, insertionNode, replacementText, lintsWithConfidence, 63 | shadowing, insertionText, cursor, entry, text, cst, agg, ast, cstLints, aggLints, astLints}, 64 | 65 | If[$Debug2, 66 | log["textDocument/codeActionFencepost: enter"] 67 | ]; 68 | 69 | id = content["id"]; 70 | 71 | If[Lookup[$CancelMap, id, False], 72 | 73 | $CancelMap[id] =.; 74 | 75 | If[$Debug2, 76 | log["canceled"] 77 | ]; 78 | 79 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 80 | ]; 81 | 82 | params = content["params"]; 83 | doc = params["textDocument"]; 84 | uri = doc["uri"]; 85 | 86 | If[Lookup[content, "stale", False] || isStale[$ContentQueue, uri], 87 | 88 | If[$Debug2, 89 | log["stale"] 90 | ]; 91 | 92 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 93 | ]; 94 | 95 | range = params["range"]; 96 | 97 | cursor = { { range["start"]["line"], range["start"]["character"] }, 98 | { range["end"]["line"], range["end"]["character"] } }; 99 | 100 | (* convert from 0-based to 1-based *) 101 | cursor+=1; 102 | 103 | If[$Debug2, 104 | log["cursor: ", ToString[cursor]] 105 | ]; 106 | 107 | entry = Lookup[$OpenFilesMap, uri, Null]; 108 | 109 | If[entry === Null, 110 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 111 | ]; 112 | 113 | cstLints = entry["CSTLints"]; 114 | 115 | (* 116 | Might get something like FileTooLarge 117 | *) 118 | If[FailureQ[cstLints], 119 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>}] 120 | ]; 121 | 122 | aggLints = entry["AggLints"]; 123 | 124 | astLints = entry["ASTLints"]; 125 | 126 | lints = cstLints ~Join~ aggLints ~Join~ astLints; 127 | 128 | If[$Debug2, 129 | log["lints: ", stringLineTake[StringTake[ToString[lints, InputForm], UpTo[1000]], UpTo[20]]]; 130 | log["...\n"] 131 | ]; 132 | 133 | lintsWithConfidence = Cases[lints, InspectionObject[_, _, _, KeyValuePattern[ConfidenceLevel -> _]]]; 134 | 135 | lints = Cases[lintsWithConfidence, InspectionObject[_, _, _, KeyValuePattern[ConfidenceLevel -> _?(GreaterEqualThan[$ConfidenceLevel])]]]; 136 | 137 | 138 | (* 139 | 140 | Disable shadow filtering for now 141 | 142 | Below is quadratic time 143 | 144 | shadowing = Select[lints, Function[lint, AnyTrue[lints, shadows[lint, #]&]]]; 145 | 146 | If[$Debug2, 147 | Write[$Messages, "shadowing: " //OutputForm, ToString[shadowing, InputForm] //OutputForm]; 148 | ]; 149 | 150 | lints = Complement[lints, shadowing]; 151 | *) 152 | 153 | (* 154 | Make sure to sort lints before taking 155 | 156 | Sort by severity, then sort by Source 157 | 158 | severityToInteger maps "Remark" -> 1 and "Fatal" -> 4, so make sure to negate that 159 | *) 160 | lints = SortBy[lints, {-severityToInteger[#[[3]]]&, #[[4, Key[Source]]]&}]; 161 | 162 | lints = Take[lints, UpTo[CodeInspector`Summarize`$DefaultLintLimit]]; 163 | 164 | 165 | lspActions = {}; 166 | 167 | Do[ 168 | 169 | diagnostics = lintToDiagnostics[lint]; 170 | 171 | If[$Debug2, 172 | log["diagnostics (up to 20): ", ToString[Take[diagnostics, UpTo[20]]]] 173 | ]; 174 | 175 | (* 176 | Need to filter the actions that match the cursor 177 | *) 178 | actions = Cases[lint, CodeAction[_, _, _], Infinity]; 179 | 180 | If[$Debug2, 181 | log["actions (up to 20): ", ToString[Take[actions, UpTo[20]]]] 182 | ]; 183 | 184 | (* 185 | Need to filter the actions that match the cursor 186 | *) 187 | actions = Cases[actions, CodeAction[_, _, KeyValuePattern[Source -> src_ /; SourceMemberIntersectingQ[src, cursor]]]]; 188 | 189 | If[$Debug2, 190 | log["actions (up to 20): ", ToString[Take[actions, UpTo[20]]]] 191 | ]; 192 | 193 | Do[ 194 | 195 | label = action[[1]]; 196 | 197 | label = plainify[label]; 198 | 199 | command = action[[2]]; 200 | actionData = action[[3]]; 201 | 202 | actionSrc = actionData[Source]; 203 | 204 | Switch[command, 205 | 206 | InsertNode, 207 | 208 | insertionNode = actionData["InsertionNode"]; 209 | 210 | If[$Debug2, 211 | log["insertionNode: ", ToString[insertionNode]] 212 | ]; 213 | 214 | (* 215 | For inserting, don't use the [start, end) range, only use [start, start) 216 | *) 217 | edit = (<| "changes"-> <| uri -> { <| "range" -> <| "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 218 | "end" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |> |>, 219 | "newText" -> ToSourceCharacterString[insertionNode] |> } |> |>)&[Map[Max[#, 0]&, actionSrc-1, {2}]]; 220 | 221 | lspAction = <| "title" -> label, 222 | "kind" -> "quickfix", 223 | "edit" -> edit, 224 | "diagnostics" -> diagnostics |>; 225 | 226 | AppendTo[lspActions, lspAction]; 227 | 228 | , 229 | 230 | InsertText, 231 | 232 | insertionText = actionData["InsertionText"]; 233 | 234 | If[$Debug2, 235 | log["insertionText: ", ToString[insertionText]]; 236 | ]; 237 | 238 | (* 239 | For inserting, don't use the [start, end) range, only use [start, start) 240 | *) 241 | edit = (<| "changes" -> <| uri -> { <| "range" -> <| "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 242 | "end" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |> |>, 243 | "newText" -> insertionText|> } |> |>)&[Map[Max[#, 0]&, actionSrc-1, {2}]]; 244 | 245 | lspAction = <| "title" -> label, 246 | "kind" -> "quickfix", 247 | "edit" -> edit, 248 | "diagnostics" -> diagnostics |>; 249 | 250 | AppendTo[lspActions, lspAction]; 251 | 252 | , 253 | 254 | DeleteNode, 255 | 256 | edit = (<| "changes"-> <| uri -> { <| "range" -> <| "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 257 | "end" -> <| "line" -> #[[2, 1]], "character" -> #[[2, 2]] |> |>, 258 | "newText" -> "" |> } |> |>)&[Map[Max[#, 0]&, actionSrc-1, {2}]]; 259 | 260 | lspAction = <| "title" -> label, 261 | "kind" -> "quickfix", 262 | "edit" -> edit, 263 | "diagnostics" -> diagnostics |>; 264 | 265 | AppendTo[lspActions, lspAction] 266 | 267 | , 268 | 269 | ReplaceNode, 270 | 271 | replacementNode = actionData["ReplacementNode"]; 272 | 273 | If[$Debug2, 274 | log["replacementNode: ", ToString[replacementNode]] 275 | ]; 276 | 277 | edit = (<| "changes"-> <| uri -> { <| "range" -> <| "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 278 | "end" -> <| "line" -> #[[2, 1]], "character" -> #[[2, 2]] |> |>, 279 | "newText" -> ToSourceCharacterString[replacementNode] |> } |> |>)&[Map[Max[#, 0]&, actionSrc-1, {2}]]; 280 | 281 | lspAction = <| "title" -> label, 282 | "kind" -> "quickfix", 283 | "edit" -> edit, 284 | "diagnostics" -> diagnostics |>; 285 | 286 | AppendTo[lspActions, lspAction] 287 | 288 | , 289 | 290 | ReplaceText, 291 | 292 | replacementText = actionData["ReplacementText"]; 293 | 294 | If[$Debug2, 295 | log["replacementText: ", ToString[replacementText]] 296 | ]; 297 | 298 | edit = (<| "changes"-> <| uri -> { <| "range" -> <| "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 299 | "end" -> <| "line" -> #[[2, 1]], "character" -> #[[2, 2]] |> |>, 300 | "newText" -> replacementText |> } |> |>)&[Map[Max[#, 0]&, actionSrc-1, {2}]]; 301 | 302 | lspAction = <| "title" -> label, 303 | "kind" -> "quickfix", 304 | "edit" -> edit, 305 | "diagnostics" -> diagnostics |>; 306 | 307 | AppendTo[lspActions, lspAction] 308 | 309 | , 310 | 311 | DeleteText, 312 | 313 | edit = (<| "changes"-> <| uri -> { <| "range" -> <| "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 314 | "end" -> <| "line" -> #[[2, 1]], "character" -> #[[2, 2]] |> |>, 315 | "newText" -> ""|> } |> |>)&[Map[Max[#, 0]&, actionSrc-1, {2}]]; 316 | 317 | lspAction = <| "title" -> label, 318 | "kind" -> "quickfix", 319 | "edit" -> edit, 320 | "diagnostics" -> diagnostics |>; 321 | 322 | AppendTo[lspActions, lspAction] 323 | 324 | , 325 | 326 | _, 327 | 328 | If[$Debug, 329 | log["UNSUPPORTED COMMAND: ", command] 330 | ] 331 | 332 | ] 333 | 334 | , 335 | {action, actions} 336 | ] 337 | 338 | , 339 | {lint, lints} 340 | ]; 341 | 342 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> lspActions |>} 343 | ]] 344 | 345 | End[] 346 | 347 | EndPackage[] 348 | -------------------------------------------------------------------------------- /LSPServer/Kernel/Definitions.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`Definitions`"] 2 | 3 | Begin["`Private`"] 4 | 5 | Needs["LSPServer`"] 6 | Needs["LSPServer`Utils`"] 7 | Needs["CodeParser`"] 8 | Needs["CodeParser`Utils`"] 9 | 10 | 11 | expandContent[content:KeyValuePattern["method" -> "textDocument/definition"], pos_] := 12 | Catch[ 13 | Module[{params, id, doc, uri}, 14 | 15 | If[$Debug2, 16 | log["textDocument/definition: enter expand"] 17 | ]; 18 | 19 | id = content["id"]; 20 | params = content["params"]; 21 | 22 | If[Lookup[$CancelMap, id, False], 23 | 24 | $CancelMap[id] =.; 25 | 26 | If[$Debug2, 27 | log["canceled"] 28 | ]; 29 | 30 | Throw[{<| "method" -> "textDocument/definitionFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 31 | ]; 32 | 33 | doc = params["textDocument"]; 34 | uri = doc["uri"]; 35 | 36 | If[isStale[$PreExpandContentQueue[[pos[[1]]+1;;]], uri], 37 | 38 | If[$Debug2, 39 | log["stale"] 40 | ]; 41 | 42 | Throw[{<| "method" -> "textDocument/definitionFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 43 | ]; 44 | 45 | <| "method" -> #, "id" -> id, "params" -> params |>& /@ { 46 | "textDocument/concreteParse", 47 | "textDocument/aggregateParse", 48 | "textDocument/abstractParse", 49 | "textDocument/definitionFencepost" 50 | } 51 | ]] 52 | 53 | handleContent[content:KeyValuePattern["method" -> "textDocument/definitionFencepost"]] := 54 | Catch[ 55 | Module[{id, params, doc, uri, ast, position, locations, line, char, cases, sym, namePat, srcs, entry}, 56 | 57 | If[$Debug2, 58 | log["textDocument/definitionFencepost: enter"] 59 | ]; 60 | 61 | id = content["id"]; 62 | 63 | If[Lookup[$CancelMap, id, False], 64 | 65 | $CancelMap[id] =.; 66 | 67 | If[$Debug2, 68 | log["canceled"] 69 | ]; 70 | 71 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 72 | ]; 73 | 74 | params = content["params"]; 75 | doc = params["textDocument"]; 76 | uri = doc["uri"]; 77 | 78 | If[Lookup[content, "stale", False] || isStale[$ContentQueue, uri], 79 | 80 | If[$Debug2, 81 | log["stale"] 82 | ]; 83 | 84 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 85 | ]; 86 | 87 | position = params["position"]; 88 | line = position["line"]; 89 | char = position["character"]; 90 | 91 | (* 92 | convert from 0-based to 1-based 93 | *) 94 | line+=1; 95 | char+=1; 96 | 97 | entry = Lookup[$OpenFilesMap, uri, Null]; 98 | 99 | If[entry === Null, 100 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 101 | ]; 102 | 103 | ast = entry["AST"]; 104 | 105 | If[FailureQ[ast], 106 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 107 | ]; 108 | 109 | (* 110 | Find the name of the symbol at the position 111 | *) 112 | cases = Cases[ast, LeafNode[Symbol, _, KeyValuePattern[Source -> src_ /; SourceMemberQ[src, {line, char}]]], Infinity]; 113 | 114 | If[cases == {}, 115 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>}] 116 | ]; 117 | 118 | sym = cases[[1]]; 119 | 120 | namePat = sym["String"]; 121 | 122 | (* 123 | Remove contexts 124 | *) 125 | namePat = StringReplace[namePat, __ ~~ "`" -> ""]; 126 | 127 | (* 128 | Definitions may be specified with or without context 129 | *) 130 | namePat = (__ ~~ "`" ~~ namePat) | namePat; 131 | 132 | cases = Flatten[Cases[ast, _[_, _, KeyValuePattern["Definitions" -> defs_ /; AnyTrue[defs, StringMatchQ[#[[2]], namePat]&]]], Infinity]]; 133 | 134 | srcs = #[[3, Key[Source]]]& /@ cases; 135 | 136 | locations = 137 | Function[{src}, 138 | <| "uri" -> uri, 139 | "range" -> <| "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 140 | "end" -> <| "line" -> #[[2, 1]], "character" -> #[[2, 2]] |> |> 141 | |>&[Map[Max[#, 0]&, src-1, {2}]]] /@ srcs; 142 | 143 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> locations |>} 144 | ]] 145 | 146 | 147 | End[] 148 | 149 | EndPackage[] 150 | -------------------------------------------------------------------------------- /LSPServer/Kernel/Diagnostics.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`Diagnostics`"] 2 | 3 | Begin["`Private`"] 4 | 5 | Needs["LSPServer`"] 6 | Needs["LSPServer`Utils`"] 7 | Needs["CodeInspector`"] 8 | Needs["CodeInspector`SuppressedRegions`"] (* for SuppressedRegions *) 9 | Needs["CodeInspector`Utils`"] 10 | Needs["CodeParser`"] 11 | Needs["CodeParser`Scoping`"] (* for scopingDataObject *) 12 | 13 | 14 | expandContent[content:KeyValuePattern["method" -> "textDocument/runDiagnostics"], pos_] := 15 | Catch[ 16 | Module[{params, doc, uri}, 17 | 18 | If[$Debug2, 19 | log["textDocument/runDiagnostics: enter expand"] 20 | ]; 21 | 22 | params = content["params"]; 23 | doc = params["textDocument"]; 24 | uri = doc["uri"]; 25 | 26 | If[isStale[$PreExpandContentQueue[[pos[[1]]+1;;]], uri], 27 | 28 | If[$Debug2, 29 | log["stale"] 30 | ]; 31 | 32 | Throw[{}] 33 | ]; 34 | 35 | <| "method" -> #, "params" -> params |>& /@ { 36 | "textDocument/concreteParse", 37 | "textDocument/suppressedRegions", 38 | "textDocument/runConcreteDiagnostics", 39 | "textDocument/aggregateParse", 40 | "textDocument/runAggregateDiagnostics", 41 | "textDocument/abstractParse", 42 | "textDocument/runAbstractDiagnostics", 43 | "textDocument/runScopingData", (* implemented in SemanticTokens.wl *) 44 | "textDocument/runScopingDiagnostics" 45 | } 46 | ]] 47 | 48 | handleContent[content:KeyValuePattern["method" -> "textDocument/suppressedRegions"]] := 49 | Catch[ 50 | Module[{params, doc, uri, entry, cst, suppressedRegions}, 51 | 52 | If[$Debug2, 53 | log["textDocument/suppressedRegions: enter"] 54 | ]; 55 | 56 | params = content["params"]; 57 | doc = params["textDocument"]; 58 | uri = doc["uri"]; 59 | 60 | If[isStale[$ContentQueue, uri], 61 | 62 | If[$Debug2, 63 | log["stale"] 64 | ]; 65 | 66 | Throw[{}] 67 | ]; 68 | 69 | entry = Lookup[$OpenFilesMap, uri, Null]; 70 | 71 | If[entry === Null, 72 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 73 | ]; 74 | 75 | suppressedRegions = Lookup[entry, "SuppressedRegions", Null]; 76 | 77 | If[suppressedRegions =!= Null, 78 | Throw[{}] 79 | ]; 80 | 81 | cst = entry["CST"]; 82 | 83 | If[$Debug2, 84 | log["before SuppressedRegions"]; 85 | ]; 86 | 87 | suppressedRegions = SuppressedRegions[cst]; 88 | 89 | If[$Debug2, 90 | log["after SuppressedRegions"] 91 | ]; 92 | 93 | If[$Debug2, 94 | log["suppressedRegions: ", suppressedRegions] 95 | ]; 96 | 97 | entry["SuppressedRegions"] = suppressedRegions; 98 | 99 | $OpenFilesMap[uri] = entry; 100 | 101 | {} 102 | ]] 103 | 104 | handleContent[content:KeyValuePattern["method" -> "textDocument/runConcreteDiagnostics"]] := 105 | Catch[ 106 | Module[{params, doc, uri, entry, cst, cstLints, suppressedRegions}, 107 | 108 | If[$Debug2, 109 | log["textDocument/runConcreteDiagnostics: enter"] 110 | ]; 111 | 112 | params = content["params"]; 113 | doc = params["textDocument"]; 114 | uri = doc["uri"]; 115 | 116 | If[isStale[$ContentQueue, uri], 117 | 118 | If[$Debug2, 119 | log["stale"] 120 | ]; 121 | 122 | Throw[{}] 123 | ]; 124 | 125 | entry = Lookup[$OpenFilesMap, uri, Null]; 126 | 127 | If[entry === Null, 128 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 129 | ]; 130 | 131 | cstLints = Lookup[entry, "CSTLints", Null]; 132 | 133 | If[cstLints =!= Null, 134 | Throw[{}] 135 | ]; 136 | 137 | cst = entry["CST"]; 138 | 139 | suppressedRegions = entry["SuppressedRegions"]; 140 | 141 | If[$Debug2, 142 | log["before CodeInspectCST"] 143 | ]; 144 | 145 | cstLints = CodeInspectCST[cst, "AggregateRules" -> <||>, "AbstractRules" -> <||>, "SuppressedRegions" -> suppressedRegions]; 146 | 147 | If[$Debug2, 148 | log["after CodeInspectCST"] 149 | ]; 150 | 151 | If[!MatchQ[cstLints, _List], 152 | log["cstLints: NOT A LIST!!!"]; 153 | Throw[{}] 154 | ]; 155 | 156 | If[$Debug2, 157 | log["cstLints: ", #["Tag"]& /@ cstLints] 158 | ]; 159 | 160 | entry["CSTLints"] = cstLints; 161 | 162 | $OpenFilesMap[uri] = entry; 163 | 164 | {} 165 | ]] 166 | 167 | handleContent[content:KeyValuePattern["method" -> "textDocument/runAggregateDiagnostics"]] := 168 | Catch[ 169 | Module[{params, doc, uri, entry, agg, aggLints, suppressedRegions}, 170 | 171 | If[$Debug2, 172 | log["textDocument/runAggregateDiagnostics: enter"] 173 | ]; 174 | 175 | params = content["params"]; 176 | doc = params["textDocument"]; 177 | uri = doc["uri"]; 178 | 179 | If[isStale[$ContentQueue, uri], 180 | 181 | If[$Debug2, 182 | log["stale"] 183 | ]; 184 | 185 | Throw[{}] 186 | ]; 187 | 188 | entry = Lookup[$OpenFilesMap, uri, Null]; 189 | 190 | If[entry === Null, 191 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 192 | ]; 193 | 194 | aggLints = Lookup[entry, "AggLints", Null]; 195 | 196 | If[aggLints =!= Null, 197 | Throw[{}] 198 | ]; 199 | 200 | agg = entry["Agg"]; 201 | 202 | suppressedRegions = entry["SuppressedRegions"]; 203 | 204 | If[$Debug2, 205 | log["before CodeInspectAgg"] 206 | ]; 207 | 208 | aggLints = CodeInspectAgg[agg, "AbstractRules" -> <||>, "SuppressedRegions" -> suppressedRegions]; 209 | 210 | If[$Debug2, 211 | log["after CodeInspectAgg"] 212 | ]; 213 | 214 | If[!MatchQ[aggLints, _List], 215 | log["aggLints: NOT A LIST!!!"]; 216 | Throw[{}] 217 | ]; 218 | 219 | If[$Debug2, 220 | log["aggLints: ", #["Tag"]& /@ aggLints] 221 | ]; 222 | 223 | entry["AggLints"] = aggLints; 224 | 225 | $OpenFilesMap[uri] = entry; 226 | 227 | {} 228 | ]] 229 | 230 | handleContent[content:KeyValuePattern["method" -> "textDocument/runAbstractDiagnostics"]] := 231 | Catch[ 232 | Module[{params, doc, uri, entry, ast, astLints, suppressedRegions}, 233 | 234 | If[$Debug2, 235 | log["textDocument/runAbstractDiagnostics: enter"] 236 | ]; 237 | 238 | params = content["params"]; 239 | doc = params["textDocument"]; 240 | uri = doc["uri"]; 241 | 242 | If[isStale[$ContentQueue, uri], 243 | 244 | If[$Debug2, 245 | log["stale"] 246 | ]; 247 | 248 | Throw[{}] 249 | ]; 250 | 251 | entry = Lookup[$OpenFilesMap, uri, Null]; 252 | 253 | If[entry === Null, 254 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 255 | ]; 256 | 257 | astLints = Lookup[entry, "ASTLints", Null]; 258 | 259 | If[astLints =!= Null, 260 | Throw[{}] 261 | ]; 262 | 263 | ast = entry["AST"]; 264 | 265 | If[FailureQ[ast], 266 | Throw[{}] 267 | ]; 268 | 269 | suppressedRegions = entry["SuppressedRegions"]; 270 | 271 | If[$Debug2, 272 | log["before CodeInspectAST"] 273 | ]; 274 | 275 | astLints = CodeInspectAST[ast, "SuppressedRegions" -> suppressedRegions]; 276 | 277 | If[$Debug2, 278 | log["after CodeInspectAST"] 279 | ]; 280 | 281 | If[!MatchQ[astLints, _List], 282 | log["astLints: NOT A LIST!!!"]; 283 | Throw[{}] 284 | ]; 285 | 286 | If[$Debug2, 287 | log["astLints: ", #["Tag"]& /@ astLints] 288 | ]; 289 | 290 | entry["ASTLints"] = astLints; 291 | 292 | $OpenFilesMap[uri] = entry; 293 | 294 | {} 295 | ]] 296 | 297 | handleContent[content:KeyValuePattern["method" -> "textDocument/runScopingDiagnostics"]] := 298 | Catch[ 299 | Module[{params, doc, uri, entry, scopingLints, scopingData, filtered, suppressedRegions, isActive}, 300 | 301 | If[$Debug2, 302 | log["textDocument/runScopingDiagnostics: enter"] 303 | ]; 304 | 305 | params = content["params"]; 306 | doc = params["textDocument"]; 307 | uri = doc["uri"]; 308 | 309 | If[isStale[$ContentQueue, uri], 310 | 311 | If[$Debug2, 312 | log["stale"] 313 | ]; 314 | 315 | Throw[{}] 316 | ]; 317 | 318 | entry = Lookup[$OpenFilesMap, uri, Null]; 319 | 320 | If[entry === Null, 321 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 322 | ]; 323 | 324 | scopingLints = Lookup[entry, "ScopingLints", Null]; 325 | 326 | If[scopingLints =!= Null, 327 | Throw[{}] 328 | ]; 329 | 330 | scopingData = entry["ScopingData"]; 331 | 332 | If[$Debug2, 333 | log["scopingData (up to 20): ", Take[scopingData, UpTo[20]]] 334 | ]; 335 | 336 | (* 337 | Filter those that have non-empty modifiers 338 | *) 339 | filtered = Cases[scopingData, scopingDataObject[_, _, {_, ___}, _]]; 340 | 341 | scopingLints = scopingDataObjectToLints /@ filtered; 342 | 343 | scopingLints = Flatten[scopingLints]; 344 | 345 | (* 346 | Filter out suppressed 347 | *) 348 | suppressedRegions = entry["SuppressedRegions"]; 349 | 350 | isActive = makeIsActiveFunc[suppressedRegions]; 351 | 352 | scopingLints = Select[scopingLints, isActive]; 353 | 354 | (* 355 | If $SemanticTokens, then only keep: 356 | errors 357 | 358 | These will be semantic highlighted AND shown in diagnostics 359 | Everything else will just be semantic highlighted 360 | 361 | 362 | If NOT $SemanticTokens, then only keep: 363 | errors 364 | unused variables 365 | 366 | Everything else, such as shadowed and unused parameters is a bit too noisy 367 | *) 368 | If[$SemanticTokens, 369 | scopingLints = 370 | Cases[scopingLints, InspectionObject[_, _, "Warning" | "Error" | "Fatal", _]] 371 | , 372 | scopingLints = 373 | Cases[scopingLints, 374 | InspectionObject[_, _, "Warning" | "Error" | "Fatal", _] | 375 | InspectionObject["UnusedVariable", _, "Scoping", _]] 376 | ]; 377 | 378 | If[$Debug2, 379 | log["scopingLints: ", #["Tag"]& /@ scopingLints] 380 | ]; 381 | 382 | entry["ScopingLints"] = scopingLints; 383 | 384 | $OpenFilesMap[uri] = entry; 385 | 386 | {} 387 | ]] 388 | 389 | 390 | handleContent[content:KeyValuePattern["method" -> "textDocument/clearDiagnostics"]] := 391 | Catch[ 392 | Module[{params, doc, uri, entry}, 393 | 394 | If[$Debug2, 395 | log["textDocument/clearDiagnostics: enter"] 396 | ]; 397 | 398 | params = content["params"]; 399 | doc = params["textDocument"]; 400 | uri = doc["uri"]; 401 | 402 | If[isStale[$ContentQueue, uri], 403 | 404 | If[$Debug2, 405 | log["stale"] 406 | ]; 407 | 408 | Throw[{}] 409 | ]; 410 | 411 | entry = Lookup[$OpenFilesMap, uri, Null]; 412 | 413 | If[entry === Null, 414 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 415 | ]; 416 | 417 | entry["CSTLints"] =.; 418 | 419 | entry["AggLints"] =.; 420 | 421 | entry["ASTLints"] =.; 422 | 423 | entry["ScopingLints"] =.; 424 | 425 | $OpenFilesMap[uri] = entry; 426 | 427 | {} 428 | ]] 429 | 430 | 431 | handleContent[content:KeyValuePattern["method" -> "textDocument/publishDiagnostics"]] := 432 | Catch[ 433 | Module[{params, doc, uri, entry, lints, lintsWithConfidence, cstLints, aggLints, astLints, scopingLints, diagnostics}, 434 | 435 | If[$Debug2, 436 | log["textDocument/publishDiagnostics: enter"] 437 | ]; 438 | 439 | params = content["params"]; 440 | doc = params["textDocument"]; 441 | uri = doc["uri"]; 442 | 443 | If[isStale[$ContentQueue, uri], 444 | 445 | If[$Debug2, 446 | log["stale"] 447 | ]; 448 | 449 | Throw[{}] 450 | ]; 451 | 452 | entry = Lookup[$OpenFilesMap, uri, Null]; 453 | 454 | (* 455 | Possibly cleared 456 | *) 457 | If[entry === Null, 458 | Throw[{<| "jsonrpc" -> "2.0", 459 | "method" -> "textDocument/publishDiagnostics", 460 | "params" -> <| "uri" -> uri, 461 | "diagnostics" -> {} |> |>}] 462 | ]; 463 | 464 | (* 465 | Possibly cleared 466 | *) 467 | cstLints = Lookup[entry, "CSTLints", {}]; 468 | 469 | (* 470 | Possibly cleared 471 | *) 472 | aggLints = Lookup[entry, "AggLints", {}]; 473 | 474 | (* 475 | Possibly cleared 476 | *) 477 | astLints = Lookup[entry, "ASTLints", {}]; 478 | 479 | (* 480 | Possibly cleared 481 | *) 482 | scopingLints = Lookup[entry, "ScopingLints", {}]; 483 | 484 | lints = cstLints ~Join~ aggLints ~Join~ astLints ~Join~ scopingLints; 485 | 486 | If[$Debug2, 487 | log["lints: ", #["Tag"]& /@ lints] 488 | ]; 489 | 490 | 491 | lintsWithConfidence = Cases[lints, InspectionObject[_, _, _, KeyValuePattern[ConfidenceLevel -> _]]]; 492 | 493 | lints = Cases[lintsWithConfidence, InspectionObject[_, _, _, KeyValuePattern[ConfidenceLevel -> _?(GreaterEqualThan[$ConfidenceLevel])]]]; 494 | 495 | (* 496 | 497 | Disable shadow filtering for now 498 | 499 | Below is quadratic time 500 | 501 | shadowing = Select[lints, Function[lint, AnyTrue[lints, shadows[lint, #]&]]]; 502 | 503 | lints = Complement[lints, shadowing]; 504 | *) 505 | 506 | 507 | (* 508 | Make sure to sort lints before taking 509 | 510 | Sort by severity, then sort by Source 511 | 512 | severityToInteger maps "Remark" -> 1 and "Fatal" -> 4, so make sure to negate that 513 | *) 514 | lints = SortBy[lints, {-severityToInteger[#[[3]]]&, #[[4, Key[Source]]]&}]; 515 | 516 | lints = Take[lints, UpTo[CodeInspector`Summarize`$DefaultLintLimit]]; 517 | 518 | If[$Debug2, 519 | log["lints: ", #["Tag"]& /@ lints] 520 | ]; 521 | 522 | diagnostics = lintToDiagnostics /@ lints; 523 | 524 | diagnostics = Flatten[diagnostics]; 525 | 526 | {<| "jsonrpc" -> "2.0", 527 | "method" -> "textDocument/publishDiagnostics", 528 | "params" -> <| "uri" -> uri, 529 | "diagnostics" -> diagnostics |> |>} 530 | ]] 531 | 532 | 533 | 534 | 535 | (* 536 | returns a function lint -> True|False 537 | *) 538 | makeIsActiveFunc[suppressedRegions_] := 539 | Function[{lint}, 540 | AllTrue[suppressedRegions, 541 | Function[{region}, 542 | !SourceMemberQ[region[[1;;2]], lint] || 543 | AllTrue[region[[3]], 544 | Function[{suppressed}, isTagActive[lint, suppressed]] 545 | ] 546 | ] 547 | ] 548 | ] 549 | 550 | 551 | isTagActive[InspectionObject[tag1_, _, _, KeyValuePattern["Argument" -> arg1_]], {tag2_, arg2_}] := 552 | !(tag1 === tag2 && arg1 === arg2) 553 | 554 | (* 555 | The lint has an Argument, but there is no argument in the suppressed 556 | *) 557 | isTagActive[InspectionObject[_, _, _, KeyValuePattern["Argument" -> _]], {_}] := 558 | True 559 | 560 | isTagActive[InspectionObject[tag1_, _, _, _], {tag2_, _}] := 561 | !(tag1 === tag2) 562 | 563 | isTagActive[InspectionObject[tag1_, _, _, _], {tag2_}] := 564 | !(tag1 === tag2) 565 | 566 | 567 | End[] 568 | 569 | EndPackage[] 570 | -------------------------------------------------------------------------------- /LSPServer/Kernel/FoldingRange.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`FoldingRange`"] 2 | 3 | Begin["`Private`"] 4 | 5 | Needs["LSPServer`"] 6 | Needs["LSPServer`DocumentSymbol`"] 7 | Needs["LSPServer`Utils`"] 8 | Needs["CodeParser`"] 9 | Needs["CodeParser`Utils`"] 10 | 11 | 12 | $FoldingRangeKind = <| 13 | (* 14 | pre-defined range kinds 15 | *) 16 | "Comment" -> "comment", 17 | "Imports" -> "imports", 18 | "Region" -> "region", 19 | (* 20 | custom range kinds 21 | *) 22 | "Function" -> "function" 23 | |> 24 | 25 | 26 | expandContent[content:KeyValuePattern["method" -> "textDocument/foldingRange"], pos_] := 27 | Catch[ 28 | Module[{params, id, doc, uri}, 29 | 30 | If[$Debug2, 31 | log["textDocument/foldingRange: enter expand"] 32 | ]; 33 | 34 | id = content["id"]; 35 | params = content["params"]; 36 | 37 | If[Lookup[$CancelMap, id, False], 38 | 39 | $CancelMap[id] =.; 40 | 41 | If[$Debug2, 42 | log["canceled"] 43 | ]; 44 | 45 | Throw[{<| "method" -> "textDocument/foldingRangeFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 46 | ]; 47 | 48 | doc = params["textDocument"]; 49 | uri = doc["uri"]; 50 | 51 | If[isStale[$PreExpandContentQueue[[pos[[1]]+1;;]], uri], 52 | 53 | If[$Debug2, 54 | log["stale"] 55 | ]; 56 | 57 | Throw[{<| "method" -> "textDocument/foldingRangeFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 58 | ]; 59 | 60 | <| "method" -> #, "id" -> id, "params" -> params |>& /@ { 61 | "textDocument/concreteParse", 62 | "textDocument/aggregateParse", 63 | "textDocument/abstractParse", 64 | "textDocument/documentNodeList", 65 | "textDocument/foldingRangeFencepost" 66 | } 67 | ]] 68 | 69 | handleContent[content:KeyValuePattern["method" -> "textDocument/foldingRangeFencepost"]] := 70 | Catch[ 71 | Module[{id, params, doc, uri, cst, entry, foldingRange, 72 | flatBag, comments, sorted, toInsert, completed, nodeList}, 73 | 74 | If[$Debug2, 75 | log["textDocument/foldingRangeFencepost: enter"] 76 | ]; 77 | 78 | id = content["id"]; 79 | 80 | If[Lookup[$CancelMap, id, False], 81 | 82 | $CancelMap[id] =.; 83 | 84 | If[$Debug2, 85 | log["canceled"] 86 | ]; 87 | 88 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 89 | ]; 90 | 91 | params = content["params"]; 92 | doc = params["textDocument"]; 93 | uri = doc["uri"]; 94 | 95 | If[isStale[$ContentQueue, uri], 96 | 97 | If[$Debug2, 98 | log["stale"] 99 | ]; 100 | 101 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 102 | ]; 103 | 104 | entry = Lookup[$OpenFilesMap, uri, Null]; 105 | 106 | If[entry === Null, 107 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 108 | ]; 109 | 110 | nodeList = Lookup[entry, "NodeList", Null]; 111 | 112 | If[nodeList === Null, 113 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 114 | ]; 115 | 116 | foldingRange = Flatten[walkOutline /@ nodeList]; 117 | 118 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> foldingRange |>} 119 | ]] 120 | 121 | 122 | 123 | walkOutline[packageCommentNode[_, children_, data_]] := 124 | Catch[ 125 | Module[{walkedChildren, src}, 126 | 127 | walkedChildren = Flatten[walkOutline /@ children]; 128 | 129 | src = data[Source]; 130 | src--; 131 | 132 | If[walkedChildren == {}, 133 | Throw[{<| 134 | "kind" -> $FoldingRangeKind["Region"], 135 | "startLine" -> src[[1, 1]], 136 | "endLine" -> src[[2, 1]] 137 | |>}] 138 | ]; 139 | 140 | {<| 141 | "kind" -> $FoldingRangeKind["Region"], 142 | "startLine" -> src[[1, 1]], 143 | "endLine" -> walkedChildren[[-1, Key["endLine"]]] 144 | |>, walkedChildren} 145 | ]] 146 | 147 | walkOutline[sectionCommentNode[name_, children_, data_]] := 148 | Catch[ 149 | Module[{walkedChildren, src}, 150 | 151 | walkedChildren = Flatten[walkOutline /@ children]; 152 | 153 | src = data[Source]; 154 | src--; 155 | 156 | If[walkedChildren == {}, 157 | Throw[{<| 158 | "kind" -> $FoldingRangeKind["Region"], 159 | "startLine" -> src[[1, 1]], 160 | "endLine" -> src[[2, 1]] 161 | |>}] 162 | ]; 163 | 164 | {<| 165 | "kind" -> $FoldingRangeKind["Region"], 166 | "startLine" -> src[[1, 1]], 167 | "endLine" -> walkedChildren[[-1, Key["endLine"]]] 168 | |>, walkedChildren} 169 | ]] 170 | 171 | walkOutline[subsectionCommentNode[name_, children_, data_]] := 172 | Catch[ 173 | Module[{walkedChildren, src}, 174 | 175 | walkedChildren = Flatten[walkOutline /@ children]; 176 | 177 | src = data[Source]; 178 | src--; 179 | 180 | If[walkedChildren == {}, 181 | Throw[{<| 182 | "kind" -> $FoldingRangeKind["Region"], 183 | "startLine" -> src[[1, 1]], 184 | "endLine" -> src[[2, 1]] 185 | |>}] 186 | ]; 187 | 188 | {<| 189 | "kind" -> $FoldingRangeKind["Region"], 190 | "startLine" -> src[[1, 1]], 191 | "endLine" -> walkedChildren[[-1, Key["endLine"]]] 192 | |>, walkedChildren} 193 | ]] 194 | 195 | walkOutline[subsubsectionCommentNode[name_, children_, data_]] := 196 | Catch[ 197 | Module[{walkedChildren, src}, 198 | 199 | walkedChildren = Flatten[walkOutline /@ children]; 200 | 201 | src = data[Source]; 202 | src--; 203 | 204 | If[walkedChildren == {}, 205 | Throw[{<| 206 | "kind" -> $FoldingRangeKind["Region"], 207 | "startLine" -> src[[1, 1]], 208 | "endLine" -> src[[2, 1]] 209 | |>}] 210 | ]; 211 | 212 | {<| 213 | "kind" -> $FoldingRangeKind["Region"], 214 | "startLine" -> src[[1, 1]], 215 | "endLine" -> walkedChildren[[-1, Key["endLine"]]] 216 | |>, walkedChildren} 217 | ]] 218 | 219 | walkOutline[functionDefinitionNode[name_, _, data_]] := 220 | Catch[ 221 | Module[{src}, 222 | 223 | src = data[Source]; 224 | src--; 225 | 226 | <| 227 | "kind" -> $FoldingRangeKind["Function"], 228 | "startLine" -> src[[1, 1]], 229 | "endLine" -> src[[2, 1]] 230 | |> 231 | ]] 232 | 233 | walkOutline[constantDefinitionNode[name_, _, data_]] := 234 | Module[{}, 235 | {} 236 | ] 237 | 238 | walkOutline[propertyDefinitionNode[name_, _, data_]] := 239 | Module[{}, 240 | {} 241 | ] 242 | 243 | walkOutline[titleComment[name_, _, data_]] := 244 | Module[{}, 245 | {} 246 | ] 247 | 248 | walkOutline[beginNode[name_, _, data_]] := 249 | Module[{}, 250 | {} 251 | ] 252 | 253 | walkOutline[endNode[Null, _, data_]] := 254 | Module[{}, 255 | {} 256 | ] 257 | 258 | walkOutline[beginPackageNode[name_, _, data_]] := 259 | Module[{}, 260 | {} 261 | ] 262 | 263 | walkOutline[endPackageNode[Null, _, data_]] := 264 | Module[{}, 265 | {} 266 | ] 267 | 268 | walkOutline[beginNewContextPathNode[ctxts_, _, data_]] := 269 | Module[{}, 270 | {} 271 | ] 272 | 273 | walkOutline[endNewContextPathNode[Null, _, data_]] := 274 | Module[{}, 275 | {} 276 | ] 277 | 278 | End[] 279 | 280 | EndPackage[] 281 | -------------------------------------------------------------------------------- /LSPServer/Kernel/Formatting.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`Formatting`"] 2 | 3 | Begin["`Private`"] 4 | 5 | Needs["LSPServer`"] 6 | Needs["LSPServer`Utils`"] 7 | Needs["CodeFormatter`"] 8 | Needs["CodeParser`"] 9 | Needs["CodeParser`Utils`"] 10 | 11 | 12 | handleContent[content:KeyValuePattern["method" -> "textDocument/formatting"]] := 13 | Catch[ 14 | Module[{params, doc, uri, id, cst, formatted, startLineCol, endLineCol, textEdit, options, tabSize, insertSpaces, 15 | indentationString, entry, text}, 16 | 17 | If[$Debug2, 18 | log["textDocument/formatting: enter"] 19 | ]; 20 | 21 | id = content["id"]; 22 | 23 | If[Lookup[$CancelMap, id, False], 24 | 25 | $CancelMap[id] =.; 26 | 27 | If[$Debug2, 28 | log["canceled"] 29 | ]; 30 | 31 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 32 | ]; 33 | 34 | params = content["params"]; 35 | doc = params["textDocument"]; 36 | uri = doc["uri"]; 37 | 38 | If[isStale[$ContentQueue, uri], 39 | 40 | If[$Debug2, 41 | log["stale"] 42 | ]; 43 | 44 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 45 | ]; 46 | 47 | options = params["options"]; 48 | tabSize = options["tabSize"]; 49 | insertSpaces = options["insertSpaces"]; 50 | 51 | entry = Lookup[$OpenFilesMap, uri, Null]; 52 | 53 | If[entry === Null, 54 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 55 | ]; 56 | 57 | text = entry["Text"]; 58 | 59 | If[$Debug2, 60 | log["before CodeConcreteParse"] 61 | ]; 62 | 63 | cst = CodeConcreteParse[text, "TabWidth" -> tabSize]; 64 | 65 | If[$Debug2, 66 | log["after CodeConcreteParse"] 67 | ]; 68 | 69 | startLineCol = cst[[2, 1, 3, Key[Source], 1]]; 70 | endLineCol = cst[[2, -1, 3, Key[Source], 2]]; 71 | 72 | (* 73 | convert from 1-based to 0-based 74 | *) 75 | startLineCol--; 76 | endLineCol--; 77 | 78 | If[insertSpaces, 79 | indentationString = StringJoin[Table[" ", {tabSize}]] 80 | , 81 | indentationString = "\t" 82 | ]; 83 | 84 | formatted = CodeFormatCST[cst, "TabWidth" -> tabSize, "IndentationString" -> indentationString]; 85 | 86 | If[FailureQ[formatted], 87 | Throw[formatted] 88 | ]; 89 | 90 | textEdit = <| "range" -> <| "start" -> <| "line" -> startLineCol[[1]], "character" -> startLineCol[[2]] |>, 91 | "end" -> <| "line" -> endLineCol[[1]], "character" -> endLineCol[[2]] |> |>, 92 | "newText" -> formatted |>; 93 | 94 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> { textEdit } |>} 95 | ]] 96 | 97 | 98 | handleContent[content:KeyValuePattern["method" -> "textDocument/rangeFormatting"]] := 99 | Catch[ 100 | Module[{params, doc, uri, id, formatted, textEdit, entry, text, options, tabSize, 101 | insertSpaces, rangeSource, lines, range, indentationString}, 102 | 103 | If[$Debug2, 104 | log["textDocument/rangeFormatting: enter"] 105 | ]; 106 | 107 | id = content["id"]; 108 | 109 | If[Lookup[$CancelMap, id, False], 110 | 111 | $CancelMap[id] =.; 112 | 113 | If[$Debug2, 114 | log["$CancelMap: ", $CancelMap] 115 | ]; 116 | 117 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 118 | ]; 119 | 120 | params = content["params"]; 121 | doc = params["textDocument"]; 122 | uri = doc["uri"]; 123 | 124 | If[isStale[$ContentQueue, uri], 125 | 126 | If[$Debug2, 127 | log["stale"] 128 | ]; 129 | 130 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 131 | ]; 132 | 133 | range = params["range"]; 134 | 135 | rangeSource = { { range["start"]["line"], range["start"]["character"] }, 136 | { range["end"]["line"], range["end"]["character"] } }; 137 | 138 | (* convert from 0-based to 1-based *) 139 | rangeSource+=1; 140 | 141 | options = params["options"]; 142 | tabSize = options["tabSize"]; 143 | insertSpaces = options["insertSpaces"]; 144 | 145 | entry = Lookup[$OpenFilesMap, uri, Null]; 146 | 147 | If[entry === Null, 148 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 149 | ]; 150 | 151 | text = entry["Text"]; 152 | 153 | lines = StringSplit[text, {"\r\n", "\n", "\r"}, All]; 154 | lines = lines[[rangeSource[[1, 1]];;rangeSource[[2, 1]]]]; 155 | If[rangeSource[[1, 1]] == rangeSource[[2, 1]], 156 | (* 157 | single line selection 158 | *) 159 | text = StringTake[lines[[1]], {rangeSource[[1, 2]], rangeSource[[2, 2]] - 1}] 160 | , 161 | (* 162 | multiple line selection 163 | *) 164 | lines[[1]] = StringDrop[lines[[1]], rangeSource[[1, 2]] - 1]; 165 | lines[[-1]] = StringTake[lines[[-1]], rangeSource[[2, 2]] - 1]; 166 | (* 167 | FIXME: use the correct newline 168 | *) 169 | text = StringJoin[Riffle[lines, "\n"]] 170 | ]; 171 | 172 | If[insertSpaces, 173 | indentationString = StringJoin[Table[" ", {tabSize}]] 174 | , 175 | indentationString = "\t" 176 | ]; 177 | 178 | formatted = CodeFormat[text, "TabWidth" -> tabSize, "IndentationString" -> indentationString]; 179 | 180 | If[FailureQ[formatted], 181 | Throw[formatted] 182 | ]; 183 | 184 | textEdit = <| "range" -> range, 185 | "newText" -> formatted |>; 186 | 187 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> { textEdit } |>} 188 | ]] 189 | 190 | End[] 191 | 192 | EndPackage[] 193 | -------------------------------------------------------------------------------- /LSPServer/Kernel/ImplicitTokens.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`ImplicitTokens`"] 2 | 3 | Begin["`Private`"] 4 | 5 | Needs["LSPServer`"] 6 | Needs["LSPServer`Utils`"] 7 | Needs["CodeInspector`Format`"] 8 | Needs["CodeInspector`ImplicitTokens`"] 9 | Needs["CodeParser`"] 10 | 11 | 12 | expandContent[content:KeyValuePattern["method" -> "textDocument/runImplicitTokens"], pos_] := 13 | Catch[ 14 | Module[{params, doc, uri}, 15 | 16 | If[$Debug2, 17 | log["textDocument/runImplicitTokens: enter expand"] 18 | ]; 19 | 20 | params = content["params"]; 21 | doc = params["textDocument"]; 22 | uri = doc["uri"]; 23 | 24 | If[isStale[$PreExpandContentQueue[[pos[[1]]+1;;]], uri], 25 | 26 | If[$Debug2, 27 | log["stale"] 28 | ]; 29 | 30 | Throw[{}] 31 | ]; 32 | 33 | <| "method" -> #, "params" -> params |>& /@ { 34 | "textDocument/concreteParse", 35 | "textDocument/aggregateParse", 36 | "textDocument/runImplicitTokensFencepost" 37 | } 38 | ]] 39 | 40 | handleContent[content:KeyValuePattern["method" -> "textDocument/runImplicitTokensFencepost"]] := 41 | Catch[ 42 | Module[{params, doc, uri, entry, cst, inspectedFileObj, implicitTokens, agg}, 43 | 44 | If[$Debug2, 45 | log["textDocument/runImplicitTokensFencepost: enter"] 46 | ]; 47 | 48 | params = content["params"]; 49 | doc = params["textDocument"]; 50 | uri = doc["uri"]; 51 | 52 | If[isStale[$ContentQueue, uri], 53 | 54 | If[$Debug2, 55 | log["stale"] 56 | ]; 57 | 58 | Throw[{}] 59 | ]; 60 | 61 | entry = Lookup[$OpenFilesMap, uri, Null]; 62 | 63 | If[entry === Null, 64 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 65 | ]; 66 | 67 | inspectedFileObj = Lookup[entry, "InspectedFileObject", Null]; 68 | 69 | If[inspectedFileObj =!= Null, 70 | Throw[{}] 71 | ]; 72 | 73 | cst = entry["CST"]; 74 | 75 | agg = entry["Agg"]; 76 | 77 | If[$Debug2, 78 | log["before CodeStructuralSyntaxAggQ"] 79 | ]; 80 | 81 | If[!CodeStructuralSyntaxAggQ[agg], 82 | 83 | entry["InspectedFileObject"] = Failure["NotCodeStructuralSyntaxAggQ", <| "URI" -> uri |>]; 84 | 85 | $OpenFilesMap[uri] = entry; 86 | 87 | Throw[{}] 88 | ]; 89 | 90 | If[$Debug2, 91 | log["after CodeStructuralSyntaxAggQ"] 92 | ]; 93 | 94 | If[$Debug2, 95 | log["before CodeInspectImplicitTokensAgg"] 96 | ]; 97 | 98 | implicitTokens = CodeInspectImplicitTokensAgg[agg, "AllowedImplicitTokens" -> $AllowedImplicitTokens]; 99 | 100 | If[$Debug2, 101 | log["after CodeInspectImplicitTokensAgg"]; 102 | log["implicitTokens (up to 20): ", Replace[Take[implicitTokens, UpTo[20]], { 103 | (* 104 | Do not print the internals 105 | *) 106 | BinaryNode[tag_, _, _] :> BinaryNode[tag, "\[Ellipsis]", "\[Ellipsis]"], 107 | InfixNode[tag_, _, _] :> InfixNode[tag, "\[Ellipsis]", "\[Ellipsis]"], 108 | PrefixNode[tag_, _, _] :> PrefixNode[tag, "\[Ellipsis]", "\[Ellipsis]"], 109 | PostfixNode[tag_, _, _] :> PostfixNode[tag, "\[Ellipsis]", "\[Ellipsis]"], 110 | TernaryNode[tag_, _, _] :> TernaryNode[tag, "\[Ellipsis]", "\[Ellipsis]"], 111 | ErrorNode[tag_, _, _] :> ErrorNode[tag, "\[Ellipsis]", "\[Ellipsis]"] 112 | }, {1} 113 | ]] 114 | ]; 115 | 116 | If[$Debug2, 117 | log["before CodeInspectImplicitTokensCSTSummarize"] 118 | ]; 119 | 120 | inspectedFileObj = CodeInspectImplicitTokensCSTSummarize[cst, implicitTokens]; 121 | 122 | If[$Debug2, 123 | log["after CodeInspectImplicitTokensCSTSummarize"] 124 | ]; 125 | 126 | entry["InspectedFileObject"] = inspectedFileObj; 127 | 128 | $OpenFilesMap[uri] = entry; 129 | 130 | {} 131 | ]] 132 | 133 | 134 | handleContent[content:KeyValuePattern["method" -> "textDocument/clearImplicitTokens"]] := 135 | Catch[ 136 | Module[{params, doc, uri, entry}, 137 | 138 | If[$Debug2, 139 | log["textDocument/clearImplicitTokens: enter"] 140 | ]; 141 | 142 | params = content["params"]; 143 | doc = params["textDocument"]; 144 | uri = doc["uri"]; 145 | 146 | If[isStale[$ContentQueue, uri], 147 | 148 | If[$Debug2, 149 | log["stale"] 150 | ]; 151 | 152 | Throw[{}] 153 | ]; 154 | 155 | entry = Lookup[$OpenFilesMap, uri, Null]; 156 | 157 | If[entry === Null, 158 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 159 | ]; 160 | 161 | entry["InspectedFileObject"] =.; 162 | 163 | $OpenFilesMap[uri] = entry; 164 | 165 | {} 166 | ]] 167 | 168 | 169 | handleContent[content:KeyValuePattern["method" -> "textDocument/publishImplicitTokens"]] := 170 | Catch[ 171 | Module[{params, doc, uri, entry, inspectedFileObj, tokens}, 172 | 173 | If[$Debug2, 174 | log["textDocument/publishImplicitTokens: enter"] 175 | ]; 176 | 177 | params = content["params"]; 178 | doc = params["textDocument"]; 179 | uri = doc["uri"]; 180 | 181 | If[isStale[$ContentQueue, uri], 182 | 183 | If[$Debug2, 184 | log["stale"] 185 | ]; 186 | 187 | Throw[{}] 188 | ]; 189 | 190 | entry = Lookup[$OpenFilesMap, uri, Null]; 191 | 192 | (* 193 | Possibly cleared 194 | *) 195 | If[entry === Null, 196 | Throw[{<| "jsonrpc" -> "2.0", 197 | "method" -> "textDocument/publishImplicitTokens", 198 | "params" -> <| "uri" -> uri, 199 | "tokens" -> {} |> |>}] 200 | ]; 201 | 202 | inspectedFileObj = Lookup[entry, "InspectedFileObject", Null]; 203 | 204 | (* 205 | Possibly cleared 206 | *) 207 | If[inspectedFileObj === Null, 208 | Throw[{<| "jsonrpc" -> "2.0", 209 | "method" -> "textDocument/publishImplicitTokens", 210 | "params" -> <| "uri" -> uri, 211 | "tokens" -> {} |> |>}] 212 | ]; 213 | 214 | (* 215 | Might get something like FileTooLarge or NotCodeStructuralSyntaxAggQ 216 | Still want to update 217 | *) 218 | If[FailureQ[inspectedFileObj], 219 | Throw[{<| "jsonrpc" -> "2.0", 220 | "method" -> "textDocument/publishImplicitTokens", 221 | "params" -> <| "uri" -> uri, 222 | "tokens" -> {} |> |>}] 223 | ]; 224 | 225 | (* 226 | Even though we have: 227 | Format[LintTimesCharacter, StandardForm] := "\[Times]" 228 | 229 | we cannot evaluate Format[LintTimesCharacter, StandardForm] to get "\[Times]" 230 | *) 231 | 232 | tokens = Flatten[ 233 | Function[{ignored1, line, content1(*, ignore rest of args*)}, 234 | MapIndexed[ 235 | Function[{sym, columnList}, 236 | <| "character" -> markupSymbolToChar[sym], "line" -> line, "column" -> columnList[[1]] |>] 237 | , 238 | content1[[2, 2;;]] 239 | ] 240 | ] @@@ inspectedFileObj[[2]] 241 | ]; 242 | 243 | tokens = DeleteCases[tokens, KeyValuePattern["character" -> " "]]; 244 | 245 | {<| "jsonrpc" -> "2.0", 246 | "method" -> "textDocument/publishImplicitTokens", 247 | "params" -> <| "uri" -> uri, 248 | "tokens" -> tokens |> |>} 249 | ]] 250 | 251 | 252 | (* 253 | convert characters to the little markup language described in notes.md 254 | 255 | Openers and closers are dropped here 256 | *) 257 | markupSymbolToChar[LintMarkup[LintAllCharacter, ___]] := "A" 258 | markupSymbolToChar[LintMarkup[LintNullCharacter, ___]] := "N" 259 | markupSymbolToChar[LintMarkup[LintOneCharacter, ___]] := "1" 260 | markupSymbolToChar[LintMarkup[LintTimesCharacter, ___]] := "x" 261 | markupSymbolToChar[LintMarkup[LintSpaceTimesCharacter, ___]] := "z" 262 | markupSymbolToChar[LintMarkup[LintExpectedOperandCharacter, ___]] := "e" 263 | 264 | markupSymbolToChar[LintMarkup[LintAllCloseCharacter, ___]] := "A" 265 | markupSymbolToChar[LintMarkup[LintAllTimesCharacter, ___]] := "B" 266 | markupSymbolToChar[LintMarkup[LintAllOneCharacter, ___]] := "D" 267 | 268 | markupSymbolToChar[LintMarkup[LintCloseCloseCharacter, ___]] := " " 269 | markupSymbolToChar[LintMarkup[LintCloseTimesCharacter, ___]] := "x" 270 | markupSymbolToChar[LintMarkup[LintOpenOneCharacter, ___]] := "1" 271 | markupSymbolToChar[LintMarkup[LintOpenOpenCharacter, ___]] := " " 272 | markupSymbolToChar[LintMarkup[LintTimesOneCharacter, ___]] := "y" 273 | markupSymbolToChar[LintMarkup[LintExpectedOperandTimesCharacter, ___]] := "f" 274 | markupSymbolToChar[LintMarkup[LintExpectedOperandCloseCharacter, ___]] := "e" 275 | markupSymbolToChar[LintMarkup[LintOpenExpectedOperandCharacter, ___]] := "e" 276 | markupSymbolToChar[LintMarkup[LintAllTimesOneCharacter, ___]] := "C" 277 | markupSymbolToChar[LintMarkup[LintCloseTimesOneCharacter, ___]] := "y" 278 | 279 | markupSymbolToChar[LintMarkup[LintUnhandledCharacter, ___]] := " " 280 | 281 | markupSymbolToChar[LintMarkup["(", ___]] := " " 282 | markupSymbolToChar[LintMarkup[")", ___]] := " " 283 | markupSymbolToChar[" "] := " " 284 | 285 | 286 | 287 | End[] 288 | 289 | EndPackage[] 290 | -------------------------------------------------------------------------------- /LSPServer/Kernel/Library.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`Library`"] 2 | 3 | SetDebug 4 | 5 | GetStartupError 6 | 7 | StartBackgroundReaderThread 8 | 9 | LockQueue 10 | 11 | UnlockQueue 12 | 13 | GetQueueSize 14 | 15 | GetFrontMessageSize 16 | 17 | PopQueue 18 | 19 | GetStdInFEOF 20 | 21 | GetStdInFError 22 | 23 | GetStdOutFEOF 24 | 25 | GetStdOutFError 26 | 27 | ReadLineFromStdIn 28 | 29 | WriteLineToStdOut 30 | 31 | WriteBytesToStdOut 32 | 33 | 34 | $LSPServerLibraryError 35 | 36 | 37 | loadAllFuncs 38 | 39 | 40 | Begin["`Private`"] 41 | 42 | Needs["LSPServer`"] 43 | Needs["LSPServer`Utils`"] 44 | Needs["CodeParser`"] 45 | 46 | 47 | $LSPServerLibraryError = <| 48 | "FREAD_FAILED" -> 1, 49 | "UNEXPECTED_LINEFEED" -> 2, 50 | "EXPECTED_LINEFEED" -> 3, 51 | "UNRECOGNIZED_HEADER" -> 4, 52 | "FWRITE_FAILED" -> 5, 53 | "FFLUSH_FAILED" -> 6 54 | |> 55 | 56 | 57 | SetDebug[level_] := 58 | Module[{res}, 59 | res = libraryFunctionWrapper[setDebug, level]; 60 | res 61 | ] 62 | 63 | GetStartupError[] := 64 | Module[{res}, 65 | res = libraryFunctionWrapper[getStartupError]; 66 | res 67 | ] 68 | 69 | StartBackgroundReaderThread[] := 70 | Module[{res}, 71 | res = libraryFunctionWrapper[startBackgroundReaderThreadFunc]; 72 | res 73 | ] 74 | 75 | LockQueue[] := 76 | Module[{res}, 77 | If[$Debug3, 78 | Print["toplevel: LockQueue before"]; 79 | ]; 80 | res = libraryFunctionWrapper[lockQueueFunc]; 81 | If[$Debug3, 82 | Print["toplevel: LockQueue after"]; 83 | ]; 84 | res 85 | ] 86 | 87 | UnlockQueue[] := 88 | Module[{res}, 89 | If[$Debug3, 90 | Print["toplevel: UnlockQueue before"]; 91 | ]; 92 | res = libraryFunctionWrapper[unlockQueueFunc]; 93 | If[$Debug3, 94 | Print["toplevel: UnlockQueue after"]; 95 | ]; 96 | res 97 | ] 98 | 99 | GetQueueSize[] := 100 | Module[{res}, 101 | res = libraryFunctionWrapper[getQueueSizeFunc]; 102 | res 103 | ] 104 | 105 | GetFrontMessageSize[] := 106 | Module[{res}, 107 | res = libraryFunctionWrapper[getFrontMessageSizeFunc]; 108 | res 109 | ] 110 | 111 | PopQueue[0] := 112 | Failure["PopQueue[0]", <||>] 113 | 114 | PopQueue[numBytes_Integer] := 115 | Module[{bytes}, 116 | bytes = ByteArray[Developer`AllocateNumericArray["UnsignedInteger8", {numBytes}]]; 117 | libraryFunctionWrapper[popQueueFunc, bytes]; 118 | bytes 119 | ] 120 | 121 | GetStdInFEOF[] := 122 | Module[{res}, 123 | res = libraryFunctionWrapper[getStdInFEOF]; 124 | res 125 | ] 126 | 127 | GetStdInFError[] := 128 | Module[{res}, 129 | res = libraryFunctionWrapper[getStdInFError]; 130 | res 131 | ] 132 | 133 | GetStdOutFEOF[] := 134 | Module[{res}, 135 | res = libraryFunctionWrapper[getStdOutFEOF]; 136 | res 137 | ] 138 | 139 | GetStdOutFError[] := 140 | Module[{res}, 141 | res = libraryFunctionWrapper[getStdOutFError]; 142 | res 143 | ] 144 | 145 | ReadLineFromStdIn[] := 146 | Module[{res}, 147 | res = libraryFunctionWrapper[readLineFromStdInFunc]; 148 | res 149 | ] 150 | 151 | WriteLineToStdOut[line_String] := 152 | Module[{res}, 153 | res = libraryFunctionWrapper[writeLineToStdOutFunc, line]; 154 | res 155 | ] 156 | 157 | WriteBytesToStdOut[bytes_ByteArray] := 158 | Module[{res}, 159 | res = libraryFunctionWrapper[writeBytesToStdOutFunc, bytes]; 160 | res 161 | ] 162 | 163 | 164 | 165 | $LSPServerLib := $LSPServerLib = 166 | Catch[ 167 | Module[{res}, 168 | 169 | res = FindLibrary["LSPServer"]; 170 | If[FailureQ[res], 171 | Throw[Failure["LSPServerNativeLibraryNotFound", <||>]] 172 | ]; 173 | res 174 | ]] 175 | 176 | 177 | 178 | loadFunc[name_String, params_, ret_] := 179 | Catch[ 180 | Module[{res, loaded, linkObject}, 181 | 182 | If[FailureQ[$LSPServerLib], 183 | Throw[$LSPServerLib] 184 | ]; 185 | 186 | If[{params, ret} =!= {LinkObject, LinkObject}, 187 | 188 | (* 189 | "regular" LibraryLink with no MathLink silliness 190 | *) 191 | loaded = LibraryFunctionLoad[$LSPServerLib, name, params, ret]; 192 | 193 | If[Head[loaded] =!= LibraryFunction, 194 | Throw[Failure["LibraryFunctionLoad", <| "Result" -> loaded |>]] 195 | ]; 196 | 197 | (* 198 | give a message and return a failure if called with arguments that do not match pattern 199 | *) 200 | With[{loaded = loaded}, 201 | Throw[ 202 | Function[ 203 | Function[{res}, 204 | If[MatchQ[res, HoldPattern[LibraryFunction[___]][___]], 205 | Message[LibraryFunction::unevaluated, loaded, {##}]; 206 | Failure["Unevaluated", <| "Function" -> loaded, "Arguments" -> {##} |>] 207 | , 208 | res 209 | ] 210 | ][loaded[##]] 211 | ] 212 | ] 213 | ] 214 | ]; 215 | 216 | (* 217 | LibraryLink creates a separate loopback link for each library function 218 | *) 219 | res = newestLinkObject[LibraryFunctionLoad[$LSPServerLib, name, params, ret]]; 220 | 221 | If[FailureQ[res], 222 | Throw[res] 223 | ]; 224 | 225 | {loaded, linkObject} = res; 226 | 227 | If[FailureQ[loaded], 228 | Throw[loaded] 229 | ]; 230 | 231 | If[Head[loaded] =!= LibraryFunction, 232 | Throw[Failure["LibraryFunctionLoad", <| "Result" -> loaded |>]] 233 | ]; 234 | 235 | (* 236 | send fully-qualified symbol names over the wire 237 | library->kernel traffic has fully-qualified symbols. 238 | This allows LibraryLink traffic to work when LSPServer` is not on $ContextPath. 239 | And we want kernel->library traffic to match this behavior, to minimize surprises. 240 | Note: this still does not enable sending fully-qualified System` symbols 241 | bug 283291 242 | bug 284492 243 | *) 244 | MathLink`LinkSetPrintFullSymbols[linkObject, True]; 245 | 246 | (* 247 | give a message and return a failure if called with arguments that do not match pattern 248 | *) 249 | With[{loaded = loaded}, 250 | Function[ 251 | Function[{res}, 252 | If[MatchQ[res, HoldPattern[LibraryFunction[___]][___]], 253 | Message[LibraryFunction::unevaluated, loaded, {##}]; 254 | Failure["Unevaluated", <| "Function" -> loaded, "Arguments" -> {##} |>] 255 | , 256 | res 257 | ] 258 | ][loaded[##]] 259 | ] 260 | ] 261 | ]] 262 | 263 | loadAllFuncs[] := ( 264 | 265 | setDebug := setDebug = loadFunc["SetDebug_LibraryLink", {Integer}, "Void"]; 266 | 267 | getStartupError := getStartupError = loadFunc["GetStartupError_LibraryLink", {}, Integer]; 268 | 269 | startBackgroundReaderThreadFunc := startBackgroundReaderThreadFunc = loadFunc["StartBackgroundReaderThread_LibraryLink", {}, "Void"]; 270 | 271 | lockQueueFunc := lockQueueFunc = loadFunc["LockQueue_LibraryLink", {}, "Void"]; 272 | 273 | unlockQueueFunc := unlockQueueFunc = loadFunc["UnlockQueue_LibraryLink", {}, "Void"]; 274 | 275 | getQueueSizeFunc := getQueueSizeFunc = loadFunc["GetQueueSize_LibraryLink", {}, Integer]; 276 | 277 | getFrontMessageSizeFunc := getFrontMessageSizeFunc = loadFunc["GetFrontMessageSize_LibraryLink", {}, Integer]; 278 | 279 | popQueueFunc := popQueueFunc = loadFunc["PopQueue_LibraryLink", { {LibraryDataType[ByteArray], "Shared"} }, "Void"]; 280 | 281 | getStdInFEOF := getStdInFEOF = loadFunc["GetStdInFEOF_LibraryLink", {}, Integer]; 282 | 283 | getStdInFError := getStdInFError = loadFunc["GetStdInFError_LibraryLink", {}, Integer]; 284 | 285 | getStdOutFEOF := getStdOutFEOF = loadFunc["GetStdOutFEOF_LibraryLink", {}, Integer]; 286 | 287 | getStdOutFError := getStdOutFError = loadFunc["GetStdOutFError_LibraryLink", {}, Integer]; 288 | 289 | readLineFromStdInFunc := readLineFromStdInFunc = loadFunc["ReadLineFromStdIn_LibraryLink", {}, "UTF8String"]; 290 | 291 | writeLineToStdOutFunc := writeLineToStdOutFunc = loadFunc["WriteLineToStdOut_LibraryLink", {"UTF8String"}, Integer]; 292 | 293 | writeBytesToStdOutFunc := writeBytesToStdOutFunc = loadFunc["WriteBytesToStdOut_LibraryLink", { {LibraryDataType[ByteArray], "Shared"} }, Integer]; 294 | ) 295 | 296 | 297 | 298 | 299 | Attributes[newestLinkObject] = {HoldFirst} 300 | 301 | (* 302 | Return the LinkObject that is created when evaluating expr along with the result of evaluating expr 303 | 304 | this is all just to find the LinkObject associated with this LibraryFunction 305 | 306 | TODO: If there is ever a nicer way to find the LinkObject, then use that 307 | *) 308 | newestLinkObject[expr_] := 309 | Catch[ 310 | Module[{before, after, res, set, first}, 311 | before = Links[]; 312 | (*evaluate*) 313 | res = expr; 314 | If[FailureQ[res], 315 | Throw[res] 316 | ]; 317 | after = Links[]; 318 | If[before == after, 319 | Throw[Failure["LinksDidNotChange", <||>]] 320 | ]; 321 | set = Complement[after, before]; 322 | If[Length[set] != 1, 323 | Throw[Failure["InternalLinksError", <| "Before" -> before, "After" -> after |>]] 324 | ]; 325 | first = set[[1]]; 326 | {res, first} 327 | ]] 328 | 329 | 330 | (* 331 | Handle the errors that may occur when calling LibraryLink functions 332 | *) 333 | libraryFunctionWrapper[libFunc_, args___] := 334 | Catch[ 335 | Module[{res}, 336 | 337 | If[FailureQ[libFunc], 338 | Throw[libFunc] 339 | ]; 340 | 341 | (* 342 | in the event of an abort, force reload of functions 343 | This will fix the transient error that can happen when an abort occurs 344 | and the next use throws LIBRARY_FUNCTION_ERROR 345 | *) 346 | CheckAbort[ 347 | res = libFunc[args]; 348 | , 349 | loadAllFuncs[]; 350 | Abort[] 351 | ]; 352 | 353 | (* 354 | There may still be a hiccup when there is a LIBRARY_FUNCTION_ERROR and the next 355 | use of the function returns unevaluated 356 | *) 357 | If[MatchQ[res, _LibraryFunctionError | Verbatim[LibraryFunction][___][___]], 358 | (* 359 | Need to specify PageWidth, or else ToString does not do anything with Short 360 | Related bugs: ? 361 | *) 362 | Throw[Failure["LibraryFunctionError", 363 | <| 364 | "ShortResult" -> ToString[Short[res], OutputForm, PageWidth -> 100], 365 | (* 366 | "ShortArguments" and "Arguments" is really just taking up space to force "FullResult" to be hidden by default 367 | *) 368 | "ShortArguments" -> ToString[Short[{args}], OutputForm, PageWidth -> 100], 369 | "Arguments" -> {args}, 370 | "FullResult" -> res 371 | |>]] 372 | ]; 373 | 374 | If[MatchQ[res, _libFunc], 375 | Throw[Failure["Unevaluated", <| "Result" -> res |>]] 376 | ]; 377 | 378 | res 379 | ]] 380 | 381 | 382 | End[] 383 | 384 | EndPackage[] 385 | -------------------------------------------------------------------------------- /LSPServer/Kernel/ListenSocket.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`ListenSocket`"] 2 | 3 | 4 | Begin["`Private`"] 5 | 6 | Needs["LSPServer`"] 7 | Needs["LSPServer`Utils`"] 8 | Needs["CodeParser`Utils`"] 9 | Needs["LSPServer`Library`"] 10 | Needs["LSPServer`Socket`"] 11 | 12 | (* ========================================================== *) 13 | (* ============== SocketListen functions ================ *) 14 | (* ========================================================== *) 15 | 16 | 17 | (* ================= Initialize ======================= *) 18 | 19 | 20 | initializeLSPComm["ListenSocket"] := SocketOpen[5555, "TCP"] 21 | 22 | 23 | 24 | 25 | (* Call-back function for SocketListen *) 26 | 27 | processData[dataByteArray_, sourceSocket_] := 28 | Module[{dataString, finalMsg, contentsIn, content, contents}, 29 | 30 | If[!ByteArrayQ[dataByteArray], 31 | 32 | log["\n\n"]; 33 | log["invalid ByteArray: ", dataByteArray]; 34 | log["\n\n"]; 35 | 36 | exitHard[] 37 | ]; 38 | 39 | dataString = ByteArrayToString @ dataByteArray; 40 | 41 | finalMsg = findMessageParts[lspMsgAssoc["msgInQueue"] <> dataString]; 42 | 43 | contentsIn = {Developer`ReadRawJSONString[lspMsgAssoc["lspMsg"]]}; 44 | 45 | expandContentsAndAppendToContentQueue[contentsIn]; 46 | 47 | ProcessScheduledJobs[]; 48 | 49 | 50 | If[empty[$ContentQueue], 51 | Pause[0.1]; 52 | Continue[] 53 | ]; 54 | 55 | While[$ContentQueue =!= {}, 56 | content = $ContentQueue[[1]]; 57 | $ContentQueue = Rest[$ContentQueue]; 58 | 59 | If[$Debug2, 60 | log["taking first from $ContentQueue: ", #["method"]&[content]]; 61 | log["rest of $ContentQueue (up to 20): ", Take[#["method"]& /@ $ContentQueue, UpTo[20]]]; 62 | log["..."] 63 | ]; 64 | 65 | contents = LSPEvaluate[content]; 66 | 67 | (* write out evaluated results to the client *) 68 | 69 | writeLSPResult["Socket", sourceSocket, contents]; 70 | 71 | ]; 72 | 73 | 74 | ] 75 | 76 | readEvalWriteLoop["ListenSocket", sock_] := SocketListen[sock, processData[#DataByteArray, #SourceSocket]&, HandlerFunctionsKeys -> {"DataByteArray", "SourceSocket"}] 77 | 78 | (* ============================ ShutDown ============================= *) 79 | shutdownLSPComm["ListenSocket", s_SocketObject] := Close[s] 80 | 81 | shutdownLSPComm["ListenSocket", _] := Null 82 | 83 | End[] 84 | 85 | EndPackage[] 86 | -------------------------------------------------------------------------------- /LSPServer/Kernel/References.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`References`"] 2 | 3 | Begin["`Private`"] 4 | 5 | Needs["LSPServer`"] 6 | Needs["LSPServer`Utils`"] 7 | Needs["CodeParser`"] 8 | Needs["CodeParser`Utils`"] 9 | 10 | 11 | expandContent[content:KeyValuePattern["method" -> "textDocument/references"], pos_] := 12 | Catch[ 13 | Module[{params, id, doc, uri}, 14 | 15 | If[$Debug2, 16 | log["textDocument/references: enter expand"] 17 | ]; 18 | 19 | id = content["id"]; 20 | params = content["params"]; 21 | 22 | If[Lookup[$CancelMap, id, False], 23 | 24 | $CancelMap[id] =.; 25 | 26 | If[$Debug2, 27 | log["canceled"] 28 | ]; 29 | 30 | Throw[{<| "method" -> "textDocument/referencesFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 31 | ]; 32 | 33 | doc = params["textDocument"]; 34 | uri = doc["uri"]; 35 | 36 | If[isStale[$PreExpandContentQueue[[pos[[1]]+1;;]], uri], 37 | 38 | If[$Debug2, 39 | log["stale"] 40 | ]; 41 | 42 | Throw[{<| "method" -> "textDocument/referencesFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 43 | ]; 44 | 45 | <| "method" -> #, "id" -> id, "params" -> params |>& /@ { 46 | "textDocument/concreteParse", 47 | "textDocument/referencesFencepost" 48 | } 49 | ]] 50 | 51 | handleContent[content:KeyValuePattern["method" -> "textDocument/referencesFencepost"]] := 52 | Catch[ 53 | Module[{id, params, doc, uri, cst, pos, line, char, cases, sym, name, srcs, entry, locations}, 54 | 55 | If[$Debug2, 56 | log["textDocument/referencesFencepost: enter"] 57 | ]; 58 | 59 | id = content["id"]; 60 | 61 | If[Lookup[$CancelMap, id, False], 62 | 63 | $CancelMap[id] =.; 64 | 65 | If[$Debug2, 66 | log["$CancelMap: ", $CancelMap] 67 | ]; 68 | 69 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 70 | ]; 71 | 72 | params = content["params"]; 73 | doc = params["textDocument"]; 74 | uri = doc["uri"]; 75 | 76 | If[Lookup[content, "stale", False] || isStale[$ContentQueue, uri], 77 | 78 | If[$Debug2, 79 | log["stale"] 80 | ]; 81 | 82 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 83 | ]; 84 | 85 | pos = params["position"]; 86 | line = pos["line"]; 87 | char = pos["character"]; 88 | 89 | (* 90 | convert from 0-based to 1-based 91 | *) 92 | line+=1; 93 | char+=1; 94 | 95 | entry = Lookup[$OpenFilesMap, uri, Null]; 96 | 97 | If[entry === Null, 98 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 99 | ]; 100 | 101 | cst = entry["CST"]; 102 | 103 | If[FailureQ[cst], 104 | Throw[cst] 105 | ]; 106 | 107 | (* 108 | Find the name of the symbol at the position 109 | *) 110 | cases = Cases[cst, LeafNode[Symbol, _, KeyValuePattern[Source -> src_ /; SourceMemberQ[src, {line, char}]]], Infinity]; 111 | 112 | If[cases == {}, 113 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>}] 114 | ]; 115 | 116 | sym = cases[[1]]; 117 | 118 | name = sym["String"]; 119 | 120 | cases = Cases[cst, LeafNode[Symbol, name, _], Infinity]; 121 | 122 | srcs = #[[3, Key[Source]]]& /@ cases; 123 | 124 | locations = 125 | Function[{src}, <| 126 | "uri" -> uri, 127 | "range" -> <| 128 | "start" -> <| "line" -> #[[1, 1]],"character" -> #[[1, 2]] |>, 129 | "end" -> <| "line" -> #[[2, 1]], "character" -> #[[2, 2]] |> 130 | |> 131 | |>&[Map[Max[#, 0]&, src-1, {2}]]] /@ srcs; 132 | 133 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> locations |>} 134 | ]] 135 | 136 | End[] 137 | 138 | EndPackage[] 139 | -------------------------------------------------------------------------------- /LSPServer/Kernel/SelectionRange.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`SelectionRange`"] 2 | 3 | Begin["`Private`"] 4 | 5 | Needs["LSPServer`"] 6 | Needs["LSPServer`Utils`"] 7 | Needs["CodeParser`"] 8 | Needs["CodeParser`Utils`"] 9 | 10 | 11 | expandContent[content:KeyValuePattern["method" -> "textDocument/selectionRange"], pos_] := 12 | Catch[ 13 | Module[{params, id, doc, uri}, 14 | 15 | If[$Debug2, 16 | log["textDocument/selectionRange: enter expand"] 17 | ]; 18 | 19 | id = content["id"]; 20 | params = content["params"]; 21 | 22 | If[Lookup[$CancelMap, id, False], 23 | 24 | $CancelMap[id] =.; 25 | 26 | If[$Debug2, 27 | log["canceled"] 28 | ]; 29 | 30 | Throw[{<| "method" -> "textDocument/selectionRangeFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 31 | ]; 32 | 33 | doc = params["textDocument"]; 34 | uri = doc["uri"]; 35 | 36 | If[isStale[$PreExpandContentQueue[[pos[[1]]+1;;]], uri], 37 | 38 | If[$Debug2, 39 | log["stale"] 40 | ]; 41 | 42 | Throw[{<| "method" -> "textDocument/selectionRangeFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 43 | ]; 44 | 45 | <| "method" -> #, "id" -> id, "params" -> params |>& /@ { 46 | "textDocument/concreteParse", 47 | "textDocument/selectionRangeFencepost" 48 | } 49 | ]] 50 | 51 | handleContent[content:KeyValuePattern["method" -> "textDocument/selectionRangeFencepost"]] := 52 | Catch[ 53 | Module[{id, params, doc, uri, entry, cst, positions, cursor, cases, firstCase, firstCaseSrc, firstCasePos, 54 | posChain, rangeify, selectionRanges}, 55 | 56 | If[$Debug2, 57 | log["textDocument/selectionRangeFencepost: enter"] 58 | ]; 59 | 60 | id = content["id"]; 61 | 62 | If[Lookup[$CancelMap, id, False], 63 | 64 | $CancelMap[id] =.; 65 | 66 | If[$Debug2, 67 | log["canceled"] 68 | ]; 69 | 70 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 71 | ]; 72 | 73 | params = content["params"]; 74 | doc = params["textDocument"]; 75 | uri = doc["uri"]; 76 | 77 | If[Lookup[content, "stale", False] || isStale[$ContentQueue, uri], 78 | 79 | If[$Debug2, 80 | log["stale"] 81 | ]; 82 | 83 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 84 | ]; 85 | 86 | positions = params["positions"]; 87 | 88 | entry = Lookup[$OpenFilesMap, uri, Null]; 89 | 90 | If[entry === Null, 91 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 92 | ]; 93 | 94 | cst = entry["CST"]; 95 | 96 | If[FailureQ[cst], 97 | Throw[cst] 98 | ]; 99 | 100 | selectionRanges = Function[{position}, 101 | 102 | cursor = { #["line"], #["character"] }&[position]; 103 | 104 | (* convert from 0-based to 1-based *) 105 | cursor+=1; 106 | 107 | cases = Cases[cst, _[_, _, KeyValuePattern[Source -> src_ /; SourceMemberQ[src, cursor]]], Infinity]; 108 | 109 | firstCase = cases[[1]]; 110 | 111 | firstCaseSrc = firstCase[[3, Key[Source]]]; 112 | 113 | firstCasePos = Position[cst, firstCase][[1]]; 114 | 115 | posChain = NestWhileList[If[ListQ[Extract[cst, #[[;; -2]]]], #[[;; -3]], #[[;; -2]]]&, firstCasePos, (Length[#] >= 2)&]; 116 | 117 | posChain = Reverse[posChain]; 118 | 119 | posChain = Rest[posChain]; 120 | 121 | If[$Debug2, 122 | log["posChain: ", posChain] 123 | ]; 124 | 125 | rangeify[pos_] := <| 126 | "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 127 | "end" -> <| "line" -> #[[2, 1]], "character" -> #[[2, 2]] |> |>&[Extract[cst, pos][[3, Key[Source]]]-1]; 128 | 129 | Fold[<| "range" -> rangeify[#2], "parent" -> #1 |>&, <| "range" -> rangeify[First[posChain]] |>, Rest[posChain]] 130 | 131 | ] /@ positions; 132 | 133 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> selectionRanges |>} 134 | ]] 135 | 136 | 137 | End[] 138 | 139 | EndPackage[] 140 | -------------------------------------------------------------------------------- /LSPServer/Kernel/SemanticTokens.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`SemanticTokens`"] 2 | 3 | $SemanticTokenTypes 4 | 5 | $SemanticTokenModifiers 6 | 7 | Begin["`Private`"] 8 | 9 | Needs["LSPServer`"] 10 | Needs["LSPServer`Utils`"] 11 | Needs["CodeParser`"] 12 | Needs["CodeParser`Scoping`"] 13 | Needs["CodeParser`Utils`"] 14 | 15 | 16 | $SemanticTokenTypes = <| 17 | "variable" -> 0, 18 | "parameter" -> 1, 19 | "function" -> 2, 20 | "constant" -> 3 21 | |> 22 | 23 | $SemanticTokenModifiers = <| 24 | "Module" -> 0, 25 | "Block" -> 1, 26 | "With" -> 2, 27 | "shadowed" -> 3, 28 | "unused" -> 4, 29 | "error" -> 5, 30 | "definition" -> 6 31 | |> 32 | 33 | 34 | expandContent[content:KeyValuePattern["method" -> "textDocument/semanticTokens/full"], pos_] := 35 | Catch[ 36 | Module[{params, id, doc, uri}, 37 | 38 | If[$Debug2, 39 | log["textDocument/semanticTokens/full: enter expand"] 40 | ]; 41 | 42 | id = content["id"]; 43 | params = content["params"]; 44 | 45 | If[Lookup[$CancelMap, id, False], 46 | 47 | $CancelMap[id] =.; 48 | 49 | If[$Debug2, 50 | log["canceled"] 51 | ]; 52 | 53 | Throw[{<| "method" -> "textDocument/semanticTokens/fullFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 54 | ]; 55 | 56 | doc = params["textDocument"]; 57 | uri = doc["uri"]; 58 | 59 | If[isStale[$PreExpandContentQueue[[pos[[1]]+1;;]], uri], 60 | 61 | If[$Debug2, 62 | log["stale"] 63 | ]; 64 | 65 | Throw[{<| "method" -> "textDocument/semanticTokens/fullFencepost", "id" -> id, "params" -> params, "stale" -> True |>}] 66 | ]; 67 | 68 | <| "method" -> #, "id" -> id, "params" -> params |>& /@ { 69 | "textDocument/concreteParse", 70 | "textDocument/aggregateParse", 71 | "textDocument/abstractParse", 72 | "textDocument/runScopingData", 73 | "textDocument/semanticTokens/fullFencepost" 74 | } 75 | ]] 76 | 77 | handleContent[content:KeyValuePattern["method" -> "textDocument/semanticTokens/fullFencepost"]] := 78 | Catch[ 79 | Module[{id, params, doc, uri, entry, semanticTokens, scopingData, transformed, 80 | line, char, oldLine, oldChar}, 81 | 82 | If[$Debug2, 83 | log["textDocument/semanticTokens/fullFencepost: enter"] 84 | ]; 85 | 86 | id = content["id"]; 87 | 88 | If[Lookup[$CancelMap, id, False], 89 | 90 | $CancelMap[id] =.; 91 | 92 | If[$Debug2, 93 | log["canceled"] 94 | ]; 95 | 96 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 97 | ]; 98 | 99 | params = content["params"]; 100 | doc = params["textDocument"]; 101 | uri = doc["uri"]; 102 | 103 | If[Lookup[content, "stale", False] || isStale[$ContentQueue, uri], 104 | 105 | If[$Debug2, 106 | log["stale"] 107 | ]; 108 | 109 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 110 | ]; 111 | 112 | entry = Lookup[$OpenFilesMap, uri, Null]; 113 | 114 | If[entry === Null, 115 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 116 | ]; 117 | 118 | semanticTokens = Lookup[entry, "SemanticTokens", Null]; 119 | 120 | If[semanticTokens =!= Null, 121 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> <| "data" -> semanticTokens |> |>}] 122 | ]; 123 | 124 | scopingData = Lookup[entry, "ScopingData", Null]; 125 | 126 | If[scopingData === Null, 127 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 128 | ]; 129 | 130 | If[FailureQ[scopingData], 131 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 132 | ]; 133 | 134 | (* 135 | transform data 136 | 137 | Related links: https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens 138 | *) 139 | 140 | transformed = 141 | Function[{source, scope, modifiers}, 142 | {#[[1, 1]], #[[1, 2]], #[[2, 2]] - #[[1, 2]], 143 | 144 | $SemanticTokenTypes[ 145 | Switch[scope, 146 | {___, "Module" | "Block" | "DynamicModule" | "Internal`InheritedBlock"}, 147 | "variable" 148 | , 149 | {___, "With"}, 150 | "constant" 151 | , 152 | {___, "Defined"}, 153 | "function" 154 | , 155 | _, 156 | "parameter" 157 | ] 158 | ], 159 | 160 | BitOr @@ BitShiftLeft[1, Lookup[$SemanticTokenModifiers, modifiers ~Join~ ( 161 | Replace[scope, 162 | { 163 | "Module" | "DynamicModule" -> "Module", 164 | "Block" | "Internal`InheritedBlock" -> "Block", 165 | "With" -> "With", 166 | _ :> Sequence @@ {} 167 | } 168 | , 169 | {1} 170 | ] 171 | )]]}&[source - 1] 172 | ] @@@ scopingData; 173 | 174 | (* 175 | Relativize the tokens 176 | *) 177 | 178 | transformed = Sort[transformed]; 179 | 180 | line = 0; 181 | char = 0; 182 | 183 | transformed = Function[{t}, 184 | oldLine = line; 185 | oldChar = char; 186 | line = t[[1]]; 187 | char = t[[2]]; 188 | {line - oldLine, If[oldLine == line, char - oldChar, char], t[[3]], t[[4]], t[[5]]} 189 | ] /@ transformed; 190 | 191 | transformed = Flatten[transformed]; 192 | 193 | semanticTokens = transformed; 194 | 195 | entry["SemanticTokens"] = semanticTokens; 196 | 197 | $OpenFilesMap[uri] = entry; 198 | 199 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> <| "data" -> semanticTokens |> |>} 200 | ]] 201 | 202 | 203 | handleContent[content:KeyValuePattern["method" -> "textDocument/runScopingData"]] := 204 | Catch[ 205 | Module[{params, doc, uri, entry, ast, scopingData}, 206 | 207 | If[$Debug2, 208 | log["textDocument/runScopingData: enter"] 209 | ]; 210 | 211 | params = content["params"]; 212 | doc = params["textDocument"]; 213 | uri = doc["uri"]; 214 | 215 | If[isStale[$ContentQueue, uri], 216 | 217 | If[$Debug2, 218 | log["stale"] 219 | ]; 220 | 221 | Throw[{}] 222 | ]; 223 | 224 | entry = Lookup[$OpenFilesMap, uri, Null]; 225 | 226 | If[entry === Null, 227 | Throw[Failure["URINotFound", <| "URI" -> uri, "OpenFilesMapKeys" -> Keys[$OpenFilesMap] |>]] 228 | ]; 229 | 230 | scopingData = Lookup[entry, "ScopingData", Null]; 231 | 232 | If[scopingData =!= Null, 233 | Throw[{}] 234 | ]; 235 | 236 | ast = entry["AST"]; 237 | 238 | If[FailureQ[ast], 239 | Throw[{}] 240 | ]; 241 | 242 | If[$Debug2, 243 | log["before ScopingData"] 244 | ]; 245 | 246 | scopingData = ScopingData[ast]; 247 | 248 | If[$Debug2, 249 | log["after ScopingData"] 250 | ]; 251 | 252 | entry["ScopingData"] = scopingData; 253 | 254 | $OpenFilesMap[uri] = entry; 255 | 256 | {} 257 | ]] 258 | 259 | 260 | End[] 261 | 262 | EndPackage[] 263 | -------------------------------------------------------------------------------- /LSPServer/Kernel/Socket.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`Socket`"] 2 | 3 | 4 | 5 | findMessageParts 6 | lspMsgAssoc 7 | writeLSPResult 8 | 9 | Begin["`Private`"] 10 | 11 | Needs["LSPServer`"] 12 | Needs["LSPServer`Utils`"] 13 | Needs["LSPServer`Library`"] 14 | Needs["CodeParser`Utils`"] 15 | 16 | (* ========================================================== *) 17 | (* ================ Socket functions ==================== *) 18 | (* ========================================================== *) 19 | 20 | lspMsgAssoc = <| "lspMsg" -> "", "msgInQueue" -> "" |> 21 | 22 | (* ================= Initialize ======================= *) 23 | 24 | (* "tcp_mode": "host", "tcp_port": 5555 *) 25 | 26 | initializeLSPComm["Socket"] := SocketConnect[5555, "TCP"] 27 | 28 | (* =============== Message validity check =============== *) 29 | 30 | checkContent[str_] := StringContainsQ[str, "Content-Length: "] 31 | 32 | checkStartPosition[str_] := First @ Flatten @ StringPosition[str, "Content"] === 1 33 | 34 | checkMsgLength[str_] := 35 | Module[{numStrs, pos, requiredLength, maxMsgLength}, 36 | (* Position of the header *) 37 | numStrs = StringCases[str, "Content-Length: " ~~ length:NumberString :> length]; 38 | requiredLength = ToExpression[First[numStrs]]; 39 | (* Extract the header*) 40 | pos = StringPosition[str, "Content-Length: " <> # <> "\r\n\r\n"]& /@ numStrs // Flatten; 41 | maxMsgLength = StringLength @ StringTake[str, {pos[[2]] + 1, StringLength[str]}]; 42 | 43 | maxMsgLength >= requiredLength 44 | ] 45 | 46 | msgContainsQ[str_] := 47 | Module[{res}, 48 | If[checkContent[str], 49 | If[checkStartPosition[str] && checkMsgLength[str], 50 | res = True; 51 | , 52 | res = False; 53 | ] 54 | , 55 | res = False; 56 | ]; 57 | res 58 | ] 59 | 60 | findMessageParts[str_] := 61 | Module[{numStrs, msgLength, headerPosition}, 62 | If[checkContent[lspMsgAssoc["msgInQueue"] <> str], 63 | If[checkStartPosition[str] && checkMsgLength[str], 64 | numStrs = First @ StringCases[str, "Content-Length: " ~~ length:NumberString :> length]; 65 | msgLength = ToExpression[numStrs]; 66 | headerPosition = Flatten @ StringPosition[str, "Content-Length: " <> numStrs <> "\r\n\r\n"]; 67 | 68 | lspMsgAssoc["lspMsg"] = StringTake[str, {headerPosition[[2]] + 1, headerPosition[[2]] + msgLength}]; 69 | lspMsgAssoc["msgInQueue"] = StringTake[str, {headerPosition[[2]] + 1 + msgLength, StringLength[str]}]; 70 | , 71 | lspMsgAssoc["msgInQueue"] = lspMsgAssoc["msgInQueue"] <> str; 72 | ] 73 | , 74 | lspMsgAssoc["msgInQueue"] = lspMsgAssoc["msgInQueue"] <> str; 75 | ]; 76 | lspMsgAssoc 77 | ] 78 | 79 | 80 | 81 | 82 | (* =================== Read ========================= *) 83 | 84 | queueEmptyQ["Socket"] := 85 | Module[{getMethods}, 86 | getMethods = #["method"]& /@ $ContentQueue; 87 | MatchQ[getMethods, {}] 88 | ] 89 | 90 | readMessage["Socket", sockObj_] := 91 | Module[{sockMessage, lspMsgEmptyQ}, 92 | (* First check if a valid msg is available in the queue *) 93 | (* When a valid message is available in the queue: No need to read new message from socket *) 94 | If[msgContainsQ[lspMsgAssoc["msgInQueue"]], 95 | 96 | If[$Debug2, 97 | log["Valid message is availble in the MessageQueue :> "]; 98 | log["\nmsgInQueue :> \n"]; 99 | log[InputForm[lspMsgAssoc["msgInQueue"]]]; 100 | ]; 101 | findMessageParts[lspMsgAssoc["msgInQueue"]]; 102 | , 103 | 104 | (* When no valid message is available in the queue: read from socket *) 105 | If[$Debug2, 106 | log["No valid message is availble in the MessageQueue :> "]; 107 | log["\nmsgInQueue :> \n"]; 108 | log[InputForm[lspMsgAssoc["msgInQueue"]]]; 109 | ]; 110 | 111 | lspMsgEmptyQ = True; 112 | While[lspMsgEmptyQ, 113 | sockMessage = SocketReadMessage[sockObj]; 114 | 115 | If[FailureQ[sockMessage], 116 | If[$Debug2, 117 | log["Read failure from client.\n"] 118 | ]; 119 | exitHard[] 120 | ]; 121 | 122 | sockMessage = ByteArrayToString[sockMessage]; 123 | lspMsgEmptyQ = Not @ msgContainsQ @ (lspMsgAssoc["msgInQueue"] <> sockMessage); 124 | (* Extract a valid message from the queue and update lspMsgAssoc *) 125 | findMessageParts[lspMsgAssoc["msgInQueue"] <> sockMessage]; 126 | ]; 127 | 128 | ]; 129 | 130 | {Developer`ReadRawJSONString[lspMsgAssoc["lspMsg"]]} 131 | ] 132 | 133 | (* Read + Expand + Update *) 134 | 135 | TryQueue["Socket", sockObj_] := 136 | Catch[ 137 | Module[{contentsIn}, 138 | If[queueEmptyQ["Socket"], 139 | If[$Debug2, 140 | log["\n\n $ContentQueue is empty, No more messages to deliver, Go to Read messages Section.\n\n "]; 141 | ] 142 | , 143 | If[$Debug2, 144 | log["\n\n $ContentQueue is not empty, No Read messages.\n\n "]; 145 | ]; 146 | Throw[Null] 147 | ]; 148 | contentsIn = readMessage["Socket", sockObj]; 149 | expandContentsAndAppendToContentQueue[contentsIn]; 150 | Null 151 | ] 152 | ] 153 | 154 | 155 | 156 | 157 | (* =================== Write ======================== *) 158 | 159 | 160 | writeSocket["Socket", socket_, header_, body_] := 161 | Module[{headerWrite, bodyWrite}, 162 | 163 | headerWrite = BinaryWrite[socket, StringToByteArray @ header]; 164 | 165 | If[ 166 | FailureQ[headerWrite], 167 | If[$Debug2, log["Message-header write failure to client."]]; 168 | exitHard[] 169 | ]; 170 | 171 | bodyWrite = BinaryWrite[socket, body]; 172 | 173 | If[ 174 | FailureQ[bodyWrite], 175 | If[$Debug2, log["Message-body write failure to client."]]; 176 | exitHard[] 177 | ] 178 | ] 179 | 180 | (* contents is a list of Associations *) 181 | writeLSPResult["Socket", sockObject_, contents_] := 182 | Module[{bytess, line}, 183 | 184 | Check[ 185 | bytess = StringToByteArray[Developer`WriteRawJSONString[#]]& /@ contents 186 | 187 | , 188 | log["\n\n"]; 189 | log["message generated by contents: ", contents]; 190 | log["\n\n"] 191 | , 192 | {Export::jsonstrictencoding} 193 | ]; 194 | 195 | 196 | (* 197 | write out each byte array in bytess 198 | *) 199 | Do[ 200 | If[!ByteArrayQ[bytes], 201 | log["\n\n"]; 202 | log["invalid bytes: ", bytes]; 203 | log["\n\n"]; 204 | exitHard[] 205 | ]; 206 | line = "Content-Length: " <> ToString[Length[bytes]] <> "\r\n\r\n"; 207 | writeSocket["Socket", sockObject, line, bytes]; 208 | , 209 | {bytes, bytess} 210 | ](*Do bytess*) 211 | ] 212 | 213 | 214 | (* ================= Read Write Loop =============================== *) 215 | readEvalWriteLoop["Socket", sock_]:= 216 | Module[{content, contents}, 217 | While[True, 218 | 219 | TryQueue["Socket", sock]; 220 | 221 | ProcessScheduledJobs[]; 222 | 223 | If[empty[$ContentQueue], 224 | Pause[0.1]; 225 | Continue[] 226 | ]; 227 | 228 | content = $ContentQueue[[1]]; 229 | $ContentQueue = Rest[$ContentQueue]; 230 | 231 | If[$Debug2, 232 | log["taking first from $ContentQueue: ", #["method"]&[content]]; 233 | log["rest of $ContentQueue (up to 20): ", Take[#["method"]& /@ $ContentQueue, UpTo[20]]]; 234 | log["..."] 235 | ]; 236 | 237 | contents = LSPEvaluate[content]; 238 | 239 | (* write out evaluated results to the client *) 240 | writeLSPResult["Socket", sock, contents]; 241 | 242 | ](*While*) 243 | ] 244 | 245 | (* ============================ ShutDown ============================= *) 246 | shutdownLSPComm["Socket", s_SocketObject] := Close[s] 247 | 248 | shutdownLSPComm["Socket", _] := Null 249 | 250 | End[] 251 | 252 | EndPackage[] 253 | -------------------------------------------------------------------------------- /LSPServer/Kernel/StdIO.wl: -------------------------------------------------------------------------------- 1 | BeginPackage["LSPServer`StdIO`"] 2 | 3 | 4 | Begin["`Private`"] 5 | 6 | Needs["LSPServer`"] 7 | Needs["LSPServer`Library`"] 8 | Needs["LSPServer`Utils`"] 9 | Needs["CodeParser`Utils`"] 10 | 11 | (* ========================================================== *) 12 | (* ================ StdIO functions ===================== *) 13 | (* ========================================================== *) 14 | 15 | 16 | 17 | (* ================= Initialize ======================= *) 18 | 19 | (* 20 | May return Null or a Failure object 21 | *) 22 | initializeLSPComm["StdIO"] := 23 | Catch[ 24 | Module[{startupError, res}, 25 | 26 | startupError = GetStartupError[]; 27 | 28 | If[FailureQ[startupError], 29 | Throw[startupError] 30 | ]; 31 | 32 | If[startupError != 0, 33 | (* 34 | For example, on Windows, running WolframKernel.exe from command prompt will give library error 1 35 | *) 36 | Throw[Failure["LSPServerNativeLibraryStartupError", <| "StartupError" -> startupError |>]] 37 | ]; 38 | 39 | res = StartBackgroundReaderThread[]; 40 | 41 | If[FailureQ[res], 42 | Throw[res] 43 | ]; 44 | 45 | Null 46 | ]] 47 | 48 | 49 | (* ================= Read Message ======================= *) 50 | 51 | TryQueue["StdIO"] := 52 | Catch[ 53 | Module[{bytes, 54 | queueSize, frontMessageSize, 55 | content, 56 | bytessIn, contentsIn}, 57 | 58 | (* 59 | NOTE: when bug 419428 is fixed and targeting 12.2 as minimum source version, then check bugfix and use WithCleanup 60 | *) 61 | (* 62 | BEGIN LOCK REGION 63 | *) 64 | LockQueue[]; 65 | 66 | queueSize = GetQueueSize[]; 67 | 68 | If[queueSize == 0, 69 | UnlockQueue[]; 70 | Throw[Null] 71 | ]; 72 | 73 | If[$Debug2, 74 | log["\n\n"]; 75 | log["messages in queue: ", queueSize]; 76 | log["\n\n"] 77 | ]; 78 | 79 | bytessIn = {}; 80 | Do[ 81 | 82 | frontMessageSize = GetFrontMessageSize[]; 83 | 84 | If[frontMessageSize == 0, 85 | 86 | UnlockQueue[]; 87 | 88 | log["\n\n"]; 89 | log["FrontMessage size was 0; shutting down"]; 90 | log["\n\n"]; 91 | 92 | exitHard[]; 93 | ]; 94 | 95 | bytes = PopQueue[frontMessageSize]; 96 | 97 | AppendTo[bytessIn, bytes] 98 | , 99 | queueSize 100 | ]; 101 | 102 | UnlockQueue[]; 103 | (* 104 | END LOCK REGION 105 | *) 106 | 107 | contentsIn = {}; 108 | Do[ 109 | If[FailureQ[bytesIn], 110 | log["\n\n"]; 111 | log["invalid bytes from stdin: ", bytesIn]; 112 | log["\n\n"]; 113 | 114 | exitHard[] 115 | ]; 116 | 117 | If[$Debug2, 118 | log["C-->S " <> ToString[Length[bytesIn]] <> " bytes"]; 119 | log["C-->S " <> stringLineTake[FromCharacterCode[Normal[Take[bytesIn, UpTo[1000]]]], UpTo[20]]]; 120 | log["...\n"] 121 | ]; 122 | 123 | content = Developer`ReadRawJSONString[ByteArrayToString[bytesIn]]; 124 | 125 | AppendTo[contentsIn, content] 126 | , 127 | {bytesIn, bytessIn} 128 | ]; 129 | 130 | bytessIn = {}; 131 | 132 | expandContentsAndAppendToContentQueue[contentsIn] 133 | 134 | ]] 135 | 136 | 137 | handleContent[content:KeyValuePattern["method" -> "stdio/error"]] := 138 | Module[{err}, 139 | 140 | err = content["code"]; 141 | 142 | logStdIOErr[err]; 143 | 144 | exitHard[] 145 | ] 146 | 147 | handleContentAfterShutdown[content:KeyValuePattern["method" -> "stdio/error"]] := 148 | Module[{err, eof}, 149 | 150 | err = content["code"]; 151 | 152 | eof = logStdIOErr[err]; 153 | 154 | If[eof, 155 | (* 156 | e.g. VSCode sends shutdown message, then closes, so this is the best we can hope for 157 | *) 158 | exitGracefully[] 159 | , 160 | exitSemiGracefully[] 161 | ] 162 | ] 163 | 164 | 165 | (* ================ Write Message ======================= *) 166 | (* contents is a list of Associations *) 167 | writeLSPResult["StdIO", sock_, contents_] := writeLSPResult["StdIO", contents] 168 | 169 | writeLSPResult["StdIO", contents_] := 170 | Module[{str, bytes, res}, 171 | 172 | (* 173 | write out each content as a byte array 174 | *) 175 | Do[ (* content *) 176 | 177 | str = Developer`WriteRawJSONString[content]; 178 | 179 | If[FailureQ[str], 180 | log["\n\n"]; 181 | log["Could not convert to JSON: ", content]; 182 | log["\n\n"]; 183 | 184 | exitHard[] 185 | ]; 186 | 187 | bytes = StringToByteArray[str]; 188 | 189 | If[!ByteArrayQ[bytes], 190 | 191 | log["\n\n"]; 192 | log["invalid bytes: ", bytes]; 193 | log["\n\n"]; 194 | 195 | exitHard[] 196 | ]; 197 | 198 | (* 199 | Write the headers 200 | *) 201 | Do[ (* line *) 202 | If[$Debug2, 203 | log[""]; 204 | log["C<--S ", line] 205 | ]; 206 | 207 | res = WriteLineToStdOut[line]; 208 | If[res =!= 0, 209 | 210 | logStdIOErr[res]; 211 | 212 | exitHard[] 213 | ] 214 | , 215 | {line, {"Content-Length: " <> ToString[Length[bytes]], ""}} 216 | ]; (* Do line *) 217 | 218 | (* 219 | Write the body 220 | *) 221 | If[$Debug2, 222 | log["C<--S ", stringLineTake[FromCharacterCode[Normal[Take[bytes, UpTo[1000]]]], UpTo[20]]]; 223 | log["...\n"] 224 | ]; 225 | 226 | res = WriteBytesToStdOut[bytes]; 227 | If[res =!= 0, 228 | 229 | logStdIOErr[res]; 230 | 231 | exitHard[] 232 | ] 233 | , 234 | {content, contents} 235 | ] (* Do content *) 236 | ] 237 | 238 | 239 | logStdIOErr[err_] := 240 | Module[{errStr, ferror, eof}, 241 | 242 | eof = False; 243 | 244 | Switch[err, 245 | $LSPServerLibraryError["FREAD_FAILED"], 246 | Which[ 247 | GetStdInFEOF[] != 0, 248 | errStr = "fread EOF"; 249 | eof = True; 250 | , 251 | (ferror = GetStdInFError[]) != 0, 252 | errStr = "fread error: " <> ToString[ferror] 253 | , 254 | True, 255 | errStr = "fread unknown error" 256 | ] 257 | , 258 | $LSPServerLibraryError["UNEXPECTED_LINEFEED"], 259 | errStr = "unexpected linefeed" 260 | , 261 | $LSPServerLibraryError["EXPECTED_LINEFEED"], 262 | errStr = "expected linefeed" 263 | , 264 | $LSPServerLibraryError["UNRECOGNIZED_HEADER"], 265 | errStr = "unrecognized header" 266 | , 267 | $LSPServerLibraryError["FWRITE_FAILED"] | $LSPServerLibraryError["FFLUSH_FAILED"], 268 | Which[ 269 | GetStdOutFEOF[] != 0, 270 | errStr = "fwrite EOF"; 271 | , 272 | (ferror = GetStdOutFError[]) != 0, 273 | errStr = "fwrite error: " <> ToString[ferror] 274 | , 275 | True, 276 | errStr = "fwrite unknown error" 277 | ] 278 | , 279 | _, 280 | errStr = "UNKNOWN ERROR: " <> ToString[err] 281 | ]; 282 | 283 | log["\n\n"]; 284 | log["StdIO Error: ", errStr]; 285 | log["\n\n"]; 286 | 287 | eof 288 | ] 289 | 290 | 291 | readEvalWriteLoop["StdIO", sock_]:= 292 | Module[{content, contents}, 293 | 294 | (* 295 | loop over: 296 | read content 297 | evaluate 298 | write content 299 | *) 300 | 301 | While[True, 302 | 303 | TryQueue["StdIO"]; 304 | 305 | ProcessScheduledJobs[]; 306 | 307 | If[empty[$ContentQueue], 308 | Pause[0.1]; 309 | Continue[] 310 | ]; 311 | 312 | content = $ContentQueue[[1]]; 313 | $ContentQueue = Rest[$ContentQueue]; 314 | 315 | If[$Debug2, 316 | log["taking first from $ContentQueue: ", #["method"]&[content]]; 317 | log["rest of $ContentQueue (up to 20): ", Take[#["method"]& /@ $ContentQueue, UpTo[20]]]; 318 | log["..."] 319 | ]; 320 | 321 | contents = LSPEvaluate[content]; 322 | 323 | (* write out evaluated results to the client *) 324 | writeLSPResult["StdIO", sock, contents]; 325 | 326 | ](*While*) 327 | ] 328 | 329 | (* ============================ ShutDown ============================= *) 330 | shutdownLSPComm["StdIO", _] := Null 331 | 332 | End[] 333 | 334 | EndPackage[] 335 | -------------------------------------------------------------------------------- /LSPServer/Kernel/Utils.wl: -------------------------------------------------------------------------------- 1 | (* ::Package::"Tags"-><|"NoVariables" -> <|"Module" -> <|Enabled -> False|>|>|>:: *) 2 | 3 | BeginPackage["LSPServer`Utils`"] 4 | 5 | normalizeURI 6 | 7 | merge 8 | 9 | stringLineTake 10 | 11 | log 12 | 13 | logFull 14 | 15 | lintToDiagnostics 16 | 17 | isStale 18 | 19 | Begin["`Private`"] 20 | 21 | Needs["LSPServer`"] 22 | Needs["CodeInspector`"] 23 | Needs["CodeInspector`Utils`"] 24 | Needs["CodeParser`"] 25 | 26 | 27 | $DiagnosticSeverity = <| 28 | "Error" -> 1, 29 | "Warning" -> 2, 30 | "Information" -> 3, 31 | "Hint" -> 4 32 | |> 33 | 34 | 35 | (* 36 | input: "file:///Users/brenton/development/stash/COD/ast/build/test.m" 37 | return: "/Users/brenton/development/stash/COD/ast/build/test.m" 38 | *) 39 | normalizeURI[uri_String] := FileNameJoin[FileNameSplit[URL[uri]]] 40 | 41 | 42 | merge[line1_Association, line2_Association] := 43 | Module[{}, 44 | If[line1["line"] =!= line2["line"], 45 | Throw[{line1, line2}, "Unhandled"] 46 | ]; 47 | If[Length[line1["characters"]] =!= Length[line2["characters"]], 48 | Throw[{line1, line2}, "Unhandled"] 49 | ]; 50 | <| "line" -> line1["line"], "characters" -> ((# /. { 51 | {a_, " "} :> a, 52 | {" ", b_} :> b, 53 | (* 54 | FIXME: arbitrarily choose the first arrow 55 | if these are the same action, then there is no problem 56 | but if one is an Insert and one is a Delete? Is that possible? 57 | maybe should choose based on probability 58 | *) 59 | {a_, b_} :> a 60 | })& /@ Transpose[{line1["characters"], line2["characters"]}]) |> 61 | ] 62 | 63 | 64 | stringLineTake[s_String, spec_] := 65 | Catch[ 66 | Module[{newlines, split}, 67 | 68 | newlines = StringCases[s, "\r\n" | "\n" | "\r"]; 69 | 70 | If[empty[newlines], 71 | Throw[s] 72 | ]; 73 | 74 | newlines = Take[newlines, spec]; 75 | 76 | split = StringSplit[s, "\r\n" | "\n" | "\r"]; 77 | 78 | split = Take[split, spec]; 79 | 80 | StringJoin[Riffle[split, newlines]] 81 | ]] 82 | 83 | 84 | timeString[] := DateString[{"Hour24", ":", "Minute", ":", "SecondExact", " "}] 85 | 86 | 87 | log[args___] := 88 | Block[{$ContextPath = {"CodeParser`", "CodeInspector`", "System`"}}, 89 | Write[$Messages, timeString[] //OutputForm, Sequence @@ (OutputForm /@ {args})] 90 | ] 91 | 92 | logFull[args___] := 93 | Block[{$ContextPath = {"CodeParser`", "CodeInspector`", "System`"}}, 94 | Write[$Messages, timeString[] //OutputForm, Sequence @@ {args}] 95 | ] 96 | 97 | 98 | lintToDiagnostics[InspectionObject[tag_, message_, severity_, data_]] := 99 | Catch[ 100 | Module[{srcs}, 101 | 102 | If[!KeyExistsQ[data, Source], 103 | (* 104 | It is possible that abstracted problems may not have Source 105 | 106 | An example would be a < b > c being abstracted as an Inequality expression 107 | 108 | Inequality is an undocumented symbol, but it does not actually show up in the source code 109 | 110 | So it would be wrong to report "Inequality is an undocumented symbol" for a < b > c 111 | *) 112 | Throw[{}] 113 | ]; 114 | 115 | srcs = { data[Source] } ~Join~ Lookup[data, "AdditionalSources", {}]; 116 | 117 | Function[{src}, (<| 118 | "code" -> If[KeyExistsQ[data, "Argument"], tag <> "\[VeryThinSpace]\:25bb\[VeryThinSpace]" <> data["Argument"], tag], 119 | "message" -> StringJoin[Riffle[plainify /@ ({message} ~Join~ Lookup[data, "AdditionalDescriptions", {}]), "\n"]], 120 | "severity" -> lintSeverityToLSPSeverity[severity], 121 | "range" -> <| 122 | "start" -> <| "line" -> #[[1, 1]], "character" -> #[[1, 2]] |>, 123 | "end" -> <| "line" -> #[[2, 1]], "character" -> #[[2, 2]] |> 124 | |>, 125 | "source" -> "wolfram lint" |>)&[Map[Max[#, 0]&, src-1, {2}]] 126 | ] /@ srcs 127 | ]] 128 | 129 | 130 | (* 131 | convert from CodeTools Lint severities to LSP severities 132 | *) 133 | lintSeverityToLSPSeverity[severity_String] := 134 | Switch[severity, 135 | "Formatting" | "ImplicitTimes" | "Scoping", $DiagnosticSeverity["Hint"], 136 | "Remark", $DiagnosticSeverity["Information"], 137 | "Warning", $DiagnosticSeverity["Warning"], 138 | "Error" | "Fatal", $DiagnosticSeverity["Error"] 139 | ] 140 | 141 | 142 | 143 | (* 144 | 145 | Are there any didChangeFenceposts or didCloseFenceposts in the queue with the same uri? 146 | 147 | Then we know that this current message is stale 148 | 149 | *) 150 | 151 | isStale[contents_, uri_] := 152 | AnyTrue[contents, 153 | MatchQ[#, KeyValuePattern[{ 154 | "method" -> "textDocument/didChangeFencepost" | "textDocument/didCloseFencepost", 155 | "params" -> KeyValuePattern["textDocument" -> KeyValuePattern["uri" -> uri]]}]]&] 156 | 157 | 158 | 159 | End[] 160 | 161 | EndPackage[] 162 | -------------------------------------------------------------------------------- /LSPServer/Kernel/Workspace.wl: -------------------------------------------------------------------------------- 1 | (* ::Package::"Tags"-><|"NoVariables" -> <|"Module" -> <|Enabled -> False|>|>|>:: *) 2 | 3 | BeginPackage["LSPServer`Workspace`"] 4 | 5 | Begin["`Private`"] 6 | 7 | Needs["LSPServer`"] 8 | Needs["LSPServer`Utils`"] 9 | Needs["CodeParser`"] 10 | Needs["CodeParser`Utils`"] 11 | 12 | 13 | handleContent[content:KeyValuePattern["method" -> "workspace/executeCommand"]] := 14 | Catch[ 15 | Module[{params, id, command}, 16 | 17 | If[$Debug2, 18 | log["workspace/executeCommand: enter"] 19 | ]; 20 | 21 | id = content["id"]; 22 | 23 | If[Lookup[$CancelMap, id, False], 24 | 25 | $CancelMap[id] =.; 26 | 27 | If[$Debug2, 28 | log["$CancelMap: ", $CancelMap] 29 | ]; 30 | 31 | Throw[{<| "jsonrpc" -> "2.0", "id" -> id, "result" -> Null |>}] 32 | ]; 33 | 34 | params = content["params"]; 35 | command = params["command"]; 36 | 37 | Switch[command, 38 | (* 39 | enable_bracket_matcher_debug_mode is an undocumented debug command 40 | *) 41 | "enable_bracket_matcher_debug_mode", 42 | $DebugBracketMatcher = True; 43 | (* 44 | TODO: trigger bracket matcher here 45 | *) 46 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>} 47 | , 48 | (* 49 | disable_bracket_matcher_debug_mode is an undocumented debug command 50 | *) 51 | "disable_bracket_matcher_debug_mode", 52 | $DebugBracketMatcher = False; 53 | (* 54 | TODO: trigger bracket matcher here 55 | *) 56 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>} 57 | , 58 | (* 59 | enable_bracket_matcher_design_colors is an undocumented debug command 60 | *) 61 | "enable_bracket_matcher_design_colors", 62 | $BracketMatcherUseDesignColors = True; 63 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>} 64 | , 65 | (* 66 | disable_bracket_matcher_design_colors is an undocumented debug command 67 | *) 68 | "disable_bracket_matcher_design_colors", 69 | $BracketMatcherUseDesignColors = False; 70 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>} 71 | , 72 | (* 73 | enable_bracket_matcher_display_insertion_text is an undocumented debug command 74 | *) 75 | "enable_bracket_matcher_display_insertion_text", 76 | $BracketMatcherDisplayInsertionText = True; 77 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>} 78 | , 79 | (* 80 | disable_bracket_matcher_display_insertion_text is an undocumented debug command 81 | *) 82 | "disable_bracket_matcher_display_insertion_text", 83 | $BracketMatcherDisplayInsertionText = False; 84 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>} 85 | , 86 | (* 87 | roundtrip_responsiveness_test is an undocumented, debug command 88 | *) 89 | "roundtrip_responsiveness_test", 90 | 91 | If[$Debug2, log["roundtrip_responsiveness_test:> \n\n"]]; 92 | If[$Debug2, log[DateString[Now, {"Year", "-", "Month", "-", "Day", "_", "Hour24", "-", "Minute", "-", "Second", "-", "Millisecond"}]]]; 93 | 94 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>, <| "method" -> "roundTripTest" |>} 95 | , 96 | (* 97 | ping_pong_responsiveness_test is an undocumented, debug command 98 | *) 99 | "ping_pong_responsiveness_test", 100 | 101 | If[$Debug2, log["ping_pong_responsiveness_test:> \n\n"]]; 102 | 103 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>, <| "method" -> "pingPongTest" |>} 104 | , 105 | (* 106 | payload_responsiveness_test is an undocumented, debug command 107 | *) 108 | "payload_responsiveness_test", 109 | 110 | If[$Debug2, log["payload_responsiveness_test:> \n\n"]]; 111 | 112 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>, <| "method" -> "payloadTest", "payload" -> StringJoin@Flatten@Table[CharacterRange["a", "z"], 100000] |>} 113 | , 114 | _, 115 | If[$Debug, 116 | log["UNSUPPORTED COMMAND: ", command] 117 | ]; 118 | {<| "jsonrpc" -> "2.0", "id" -> id, "result" -> {} |>} 119 | ] 120 | ]] 121 | 122 | 123 | handleContent[content:KeyValuePattern["method" -> "workspace/didChangeWatchedFiles"]] := 124 | Module[{}, 125 | 126 | If[$Debug2, 127 | log["workspace/didChangeWatchedFiles: enter"] 128 | ]; 129 | 130 | {} 131 | ] 132 | 133 | handleContent[content:KeyValuePattern["method" -> "workspace/didChangeConfiguration"]] := 134 | Module[{}, 135 | 136 | If[$Debug2, 137 | log["workspace/didChangeConfiguration: enter"] 138 | ]; 139 | 140 | {} 141 | ] 142 | 143 | 144 | End[] 145 | 146 | EndPackage[] 147 | -------------------------------------------------------------------------------- /LSPServer/PacletInfo.wl.in: -------------------------------------------------------------------------------- 1 | 2 | Paclet[ 3 | Name -> "LSPServer", 4 | Version -> "1.9", 5 | WolframVersion -> "12.1+", 6 | Description -> "Language Server Protocol implementation for Wolfram Language.", 7 | Creator -> "Brenton Bostick ", 8 | BuildDate -> "", 9 | BuildNumber -> 0, 10 | BuildWolframVersionNumber -> 0, 11 | BuildWolframLibraryVersion -> 0, 12 | Updating -> Automatic, 13 | Extensions -> { 14 | {"Kernel", Root -> "Kernel", Context -> "LSPServer`"}, 15 | {"Documentation", Language -> All, MainPage -> "Guides/LSPServer"}, 16 | {"LibraryLink"}, 17 | {"Resource", Root -> "Resources", 18 | Resources -> { 19 | {"AnalyzableMessages", "Data/AnalyzableMessages.wl"}, 20 | {"BuiltinFunctions", "Data/BuiltinFunctions.wl"}, 21 | {"Options", "Data/Options.wl"}, 22 | {"Constants", "Data/Constants.wl"}, 23 | {"ExperimentalSymbols", "Data/ExperimentalSymbols.wl"}, 24 | {"FreeCharacters", "Data/FreeCharacters.wl"}, 25 | {"FreeLongNames", "Data/FreeLongNames.wl"}, 26 | {"ObsoleteSymbols", "Data/ObsoleteSymbols.wl"}, 27 | {"SystemCharacters", "Data/SystemCharacters.wl"}, 28 | {"SystemLongNames", "Data/SystemLongNames.wl"}, 29 | {"UndocumentedCharacters", "Data/UndocumentedCharacters.wl"}, 30 | {"UndocumentedLongNames", "Data/UndocumentedLongNames.wl"}, 31 | {"UndocumentedSymbols", "Data/UndocumentedSymbols.wl"}, 32 | {"UnsupportedCharacters", "Data/UnsupportedCharacters.wl"}, 33 | {"UnsupportedLongNames", "Data/UnsupportedLongNames.wl"} 34 | } 35 | } 36 | } 37 | ] 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LSPServer 2 | 3 | LSPServer is a package that implements the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) for Wolfram Language and allows a Wolfram Language kernel to run as an LSP server. 4 | 5 | [Developing Wolfram Language Code in Other Editors and IDEs with LSP from WTC 2021: Watch Video (youtube)](https://www.youtube.com/watch?v=nXVEOUMZbzQ) 6 | 7 | LSPServer implements several LSP features: 8 | * Code diagnostics 9 | * Suggestions for fixes 10 | * Formatting files and selections 11 | * Semantic highlighting 12 | * Expand / shrink selection 13 | * Outline 14 | * Color swatches 15 | * Symbol references 16 | * Documentation on hover 17 | 18 | This repo is for users who are interested in adding LSP support for Wolfram Language to LSP clients. 19 | 20 | There are official Wolfram LSP clients for [Sublime Text](https://github.com/WolframResearch/Sublime-WolframLanguage) and [Visual Studio Code](https://github.com/WolframResearch/vscode-wolfram). 21 | 22 | 23 | ## Setup 24 | 25 | LSPServer depends on [CodeParser paclet](https://github.com/WolframResearch/codeparser), [CodeInspector paclet](https://github.com/WolframResearch/codeinspector), and [CodeFormatter paclet](https://github.com/WolframResearch/codeformatter). 26 | 27 | LSPServer and its dependencies are included in Mathematica 13.0 and above. 28 | 29 | Install LSPServer paclet and dependencies from the public paclet server: 30 | ``` 31 | PacletInstall["CodeParser"] 32 | PacletInstall["CodeInspector"] 33 | PacletInstall["CodeFormatter"] 34 | PacletInstall["LSPServer"] 35 | ``` 36 | 37 | [Build and install the LSPServer paclet locally](HowToBuild.md) 38 | 39 | 40 | ## Using LSPServer 41 | 42 | 99% of users will not need to worry about using LSPServer directly. LSPServer is used internally when an LSP client launches a Wolfram kernel as an LSP server. This all happens in the background. 43 | 44 | But it can be useful to run LSPServer when developing a new LSP client. 45 | 46 | Create a file named server.wl: 47 | ``` 48 | Needs["LSPServer`"] 49 | 50 | StartServer[] 51 | ``` 52 | 53 | And run from the command-line: 54 | ``` 55 | brenton@brenton2maclap % WolframKernel -noprompt -run Get\[\"server.wl\"\] 56 | 14:03:48.605 $CommandLine: {WolframKernel, -noprompt, -run, Get["server.wl"]} 57 | 14:03:48.607 58 | 59 | 60 | 14:03:48.608 $commProcess: StdIO 61 | 14:03:48.608 62 | 63 | 64 | 14:03:48.608 $ProcessID: 54603 65 | 14:03:48.609 66 | 67 | 68 | 14:03:48.609 $ParentProcessID: 54582 69 | 14:03:48.609 70 | 71 | 72 | 14:03:48.609 Starting server... (If this is the last line you see, then StartServer[] may have been called in an unexpected way and the server is hanging.) 73 | 14:03:48.610 74 | ``` 75 | 76 | Notice the proper character escapes on the command-line. 77 | 78 | The kernel process is blocked waiting on input to its stdin. 79 | 80 | Properly formed LSP JSON-RPC can be sent to the kernel, and the kernel would send its response to stdout. 81 | 82 | 83 | ## Troubleshooting 84 | 85 | Make sure that the paclets can be found on your system: 86 | ``` 87 | Needs["LSPServer`"] 88 | ``` 89 | 90 | You may get `LibraryFunction` messages: 91 | ``` 92 | 14:49:15.663 $CommandLine: {/Applications/Mathematica.app/Contents/MacOS/WolframKernel, -noinit, -noprompt, -nopaclet, -nostartuppaclets, -noicon, -run, Needs["LSPServer`"];LSPServer`StartServer["/Users/user/logs/"]} 93 | 14:49:15.664 94 | 95 | 96 | 14:49:15.664 $commProcess: StdIO 97 | 14:49:15.664 98 | 99 | 100 | 14:49:15.665 $ProcessID: 22400 101 | 14:49:15.665 102 | 103 | 104 | 14:49:15.666 $ParentProcessID: 22394 105 | 14:49:15.666 106 | 107 | 108 | 14:49:15.667 Directory[]: /private/var/folders/90/4fbnjdqx3f791xb65c02fm2m000bfy/T/Wolfram-LSPServer 109 | 14:49:15.667 110 | 111 | 112 | 14:49:15.668 Starting server... (If this is the last line you see, then StartServer[] may have been called in an unexpected way and the server is hanging.) 113 | 14:49:15.668 114 | 115 | 116 | 117 | LibraryFunction::version: The version number 7 of the library is not consistent with the current or any previous WolframLibraryVersion. 118 | 119 | LibraryFunction::initerr: A nonzero error code 7 was returned during the initialization of the library /Users/user/Library/Mathematica/Paclets/Repository/LSPServer-1.6/LibraryResources/MacOSX-x86-64/LSPServer.dylib. 120 | 121 | LibraryFunction::libload: The function GetStartupError_LibraryLink was not loaded from the file /Users/user/Library/Mathematica/Paclets/Repository/LSPServer-1.6/LibraryResources/MacOSX-x86-64/LSPServer.dylib. 122 | 14:49:16.129 123 | 124 | 125 | 14:49:16.129 Initialization failed: Failure["LibraryFunctionLoad", <|"Result" -> $Failed|>] 126 | 14:49:16.484 127 | 128 | 129 | 14:49:16.485 Language Server kernel did not shutdown properly. 130 | 14:49:16.485 131 | 14:49:16.487 This is the command that was used: 132 | 14:49:16.488 {/Applications/Mathematica.app/Contents/MacOS/WolframKernel, -noinit, -noprompt, -nopaclet, -nostartuppaclets, -noicon, -run, Needs["LSPServer`"];LSPServer`StartServer["/Users/user/logs/"]} 133 | 14:49:16.488 134 | 14:49:16.489 To help diagnose the problem, run this in a notebook: 135 | Needs["LSPServer`"] 136 | LSPServer`RunServerDiagnostic[{"/Applications/Mathematica.app/Contents/MacOS/WolframKernel", "-noinit", "-noprompt", "-nopaclet", "-nostartuppaclets", "-noicon", "-run", "Needs[\"LSPServer`\"];LSPServer`StartServer[\"/Users/user/logs/\"]"}] 137 | 14:49:16.490 138 | 14:49:16.490 Fix any problems then restart and try again. 139 | 14:49:16.491 140 | 141 | 142 | 14:49:16.492 KERNEL IS EXITING HARD 143 | 14:49:16.492 144 | ``` 145 | 146 | This means that LSPServer was built with a newer version of Wolfram System than your system supports. 147 | 148 | To fix this, build LSPServer from source with the version of Wolfram System that you will use. 149 | 150 | 151 | ### Server settings 152 | 153 | Turn on debug logging from the kernel. 154 | 155 | Give a string argument to StartServer[]. This is a directory that kernel logs will be written to. 156 | 157 | ``` 158 | Needs["LSPServer`"];LSPServer`StartServer["/path/to/log/directory/"] 159 | ``` 160 | -------------------------------------------------------------------------------- /Tests/ScopingExamples.wl: -------------------------------------------------------------------------------- 1 | 2 | 3 | Module[{a}, 4 | (* ^ scoped *) 5 | a 6 | (* ^ scoped *) 7 | ] 8 | 9 | 10 | Module[{a}, 11 | (* ^ unused *) 12 | Module[{a}, 13 | (* ^ scoped, shadowed *) 14 | a 15 | (* ^ scoped, shadowed *) 16 | ] 17 | ] 18 | 19 | 20 | g[x_] := 21 | (*^ scoped *) 22 | Module[{a}, 23 | (* ^ scoped *) 24 | a + x 25 | (* ^ scoped *) 26 | (* ^ scoped *) 27 | ] 28 | 29 | 30 | he:ThreeJSymbol[{j1_, m1_}]:= {j1, m1} 31 | (*^ unused *) 32 | (* ^ scoped *) 33 | (* ^ scoped *) 34 | (* ^ scoped *) 35 | (* ^ scoped *) 36 | 37 | 38 | Table[i, {i, nblks}] 39 | (* ^ scoped *) 40 | (* ^ scoped *) 41 | (* ^ NOT scoped*) 42 | 43 | Table[i, {i, nblks}, {blksize}] 44 | (* ^ scoped *) 45 | (* ^ scoped *) 46 | (* ^ NOT scoped*) 47 | (* ^ NOT scoped*) 48 | 49 | 50 | Block[{pred}, 51 | (* ^ unused *) 52 | Function[{pred}, 53 | (* ^ shadowed *) 54 | pred 55 | (* ^ shadowed *) 56 | ] 57 | ] 58 | 59 | Block[{pred}, 60 | pred_ :> pred 61 | (* ^ shadowed *) 62 | (* ^ shadowed *) 63 | ] 64 | 65 | 66 | Compile[{{rows, _Integer, 0}, {cols, _Integer, 0}}, 67 | (* ^ scoped *) 68 | (* ^ scoped *) 69 | Table[cols*(i - 1) + j, {i, rows}, {j, cols}] 70 | (* ^ scoped *) 71 | (* ^ scoped *) 72 | (* ^ scoped *) 73 | ] 74 | 75 | 76 | Module[{func}, 77 | (* ^ scoped *) 78 | func /: Map[func, _] := lst; 79 | (*^ scoped *) 80 | (* ^ scoped *) 81 | ] 82 | 83 | 84 | Compile[{{staterules,_Integer,2},{colorrules,_Integer,2},{positionrules,_Integer,3},{steps,_Integer}}, 85 | (* ^ unused *) 86 | (* ^ unused *) 87 | (* ^ unused *) 88 | (* ^ unused *) 89 | body 90 | , 91 | {{Extract[tapelist[[step]],position],_Integer}} 92 | ] 93 | 94 | 95 | Module[{a}, 96 | (* ^ unused*) 97 | aa 98 | (* ^ NOT scoped*) 99 | ] 100 | 101 | 102 | Function[{a}, aa] 103 | (* ^ unused*) 104 | (* ^ NOT scoped*) 105 | 106 | 107 | Do[5 + 6, {i, 8, 9}] 108 | (* ^ unused*) 109 | 110 | 111 | f[a_] := 4 + 3 112 | (*^ unused*) 113 | 114 | 115 | f[args___] := {args} 116 | (*^ scoped*) 117 | (* ^ scoped*) 118 | 119 | 120 | Module[{a}, 121 | (* ^ scoped*) 122 | With[{a = a}, 123 | (* ^ unused, shadowed*) 124 | (* ^ scoped*) 125 | j 126 | ] 127 | ] 128 | 129 | 130 | Function[Null, Function[Null, Null, {HoldAllComplete}], {HoldAllComplete}] 131 | (* ^ NOT semantic highlighted*) 132 | (* ^ NOT semantic highlighted*) 133 | (* ^ NOT semantic highlighted*) 134 | 135 | 136 | With[{baseTy = baseTy1}, 137 | (* ^ scoped*) 138 | (* ^ NOT scoped*) 139 | Function[{Typed[arg,"ArrayList"[baseTy]], Typed[elem, baseTy]}, 140 | (* ^ scoped*) 141 | (* ^ scoped*) 142 | (* ^ scoped*) 143 | (* ^ scoped*) 144 | Append[arg, elem] 145 | (* ^ scoped*) 146 | (* ^ scoped*) 147 | ] 148 | ] 149 | 150 | 151 | 152 | 153 | Do[ 154 | {i, j} 155 | (* ^ scoped*) 156 | (* ^ scoped*) 157 | , 158 | {i, 1, j} 159 | (* ^ scoped*) 160 | (* ^ NOT scoped*) 161 | , 162 | {j, 1, i} 163 | (* ^ scoped*) 164 | (* ^ scoped*) 165 | ] 166 | 167 | 168 | 169 | 170 | 171 | 172 | (* 173 | Condition 174 | *) 175 | 176 | g[x_] /; MatchQ[x, g[a_]] := x 177 | (*^ scoped*) 178 | (* ^ scoped*) 179 | (* ^ NOT semantic highlighted*) 180 | (* ^ scoped*) 181 | 182 | 183 | g[x_] /; MatchQ[y, g[a_]] := z 184 | (*^ unused*) 185 | (* ^ NOT scoped*) 186 | (* ^ NOT scoped*) 187 | (* ^ NOT scoped*) 188 | 189 | 190 | g[x_] /; MatchQ[x, g[a_]] := 191 | (*^ scoped*) 192 | (* ^ scoped *) 193 | (* ^ NOT semantic highlighted *) 194 | Module[{a}, 195 | (* ^ scoped *) 196 | a 197 | (* ^ scoped *) 198 | ] 199 | 200 | 201 | f[args___ /; goodQ[args]] := {args} 202 | (*^ scoped*) 203 | (* ^ scoped*) 204 | (* ^ scoped*) 205 | 206 | 207 | f[args___] := {args} /; goodQ[args] 208 | (*^ scoped*) 209 | (* ^ scoped*) 210 | (* ^ scoped*) 211 | 212 | 213 | Module[{x}, 214 | g[x_] /; MatchQ[x, g[a_]] := x 215 | (* ^ shadowed*) 216 | (* ^ shadowed*) 217 | (* ^ NOT scoped*) 218 | (* ^ shadowed*) 219 | ] 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | (* 229 | NOT WORKING YET: 230 | *) 231 | 232 | Switch[x, 233 | a_ /; f[a], 234 | (* ^ scoped*) 235 | (* ^ scoped*) 236 | 6 237 | ] 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | g[x_] := k[x_] 247 | (* ^ scoped*) 248 | (* ^ error*) 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | (* 257 | NOT WORKING YET: 258 | *) 259 | 260 | g[x_] /; MatchQ[x, g[a_] /; h[a]] := x + a 261 | (* ^ scoped*) 262 | (* ^ scoped*) 263 | (* ^ scoped*) 264 | (* ^ scoped*) 265 | (* ^ scoped*) 266 | (* ^ NOT scoped*) 267 | 268 | 269 | 270 | 271 | 272 | g[x_] /; MatchQ[x, g[a_] /; h[a]] := x + k[a_] 273 | (* ^ scoped*) 274 | (* ^ scoped*) 275 | (* ^ scoped*) 276 | (* ^ scoped*) 277 | (* ^ scoped*) 278 | (* ^ NOT scoped and no error*) 279 | 280 | 281 | 282 | 283 | 284 | 285 | (* 286 | Non-linear patterns 287 | *) 288 | 289 | sqlEqual[a_, a_] := True 290 | (* ^ scoped*) 291 | (* ^ scoped*) 292 | 293 | 294 | 295 | 296 | 297 | 298 | (* 299 | Sum 300 | *) 301 | 302 | Sum[k + j, {k,0,Infinity}, {j, 1, 2}] 303 | (* ^ scoped*) 304 | (* ^ scoped*) 305 | (* ^ scoped*) 306 | (* ^ scoped*) 307 | 308 | 309 | Sum[Pochhammer[a,k] * Pochhammer[b,k] * x^k/(Pochhammer[c,k] * k!) + j, 310 | (* ^ scoped*) 311 | (* ^ scoped*) 312 | (* ^ scoped*) 313 | (* ^ scoped*) 314 | (* ^ scoped*) 315 | {k,0,Infinity}, {j, 1, 2}, VerifyConvergence->False + k + j] 316 | (* ^ scoped*) 317 | (* ^ NOT scoped*) 318 | (* ^ NOT scoped*) 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | (* 327 | NOT WORKING YET: 328 | *) 329 | 330 | Experimental`OptimizedExpression /: 331 | (*^ scoped *) 332 | HoldPattern[ SetDelayed[ lhs_, Experimental`OptimizedExpression[body_] ] ] := 333 | (* ^ scoped*) 334 | (* ^ scoped *) 335 | (* ^ scoped*) 336 | lhs := body; 337 | (* ^ scoped*) 338 | (* ^ scoped*) 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | xxx /: Part[xxx, yyy] := foo 352 | (* ^ it is convenient to mark Part as having a definition here *) 353 | bar[fileInfo_] := Block[{path = fileInfo[[67]]}, jjj] 354 | (* ^ and then asking for the Source of the Part syntax here was causing a crash*) 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | interpreter_Interpreter[input_:Missing["NoInput"]] := s 364 | (*^ unused*) 365 | (* ^ defined*) 366 | (* ^ unused*) 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | (#)& 375 | (*^ scoped*) 376 | 377 | ((#)&)& 378 | (*^ shadowed*) 379 | 380 | 381 | 382 | ((##234)&)& 383 | (*^ shadowed*) 384 | 385 | 386 | Function[# + 1] 387 | 388 | 389 | # 390 | (*^ error*) 391 | 392 | 393 | 394 | 395 | 396 | 397 | CurrentValue /: (CurrentValue[target_, option_] = value_) := With[{t=target, o=option}, 398 | MathLink`CallFrontEndHeld[FrontEnd`SetValue[FEPrivate`Set[CurrentValue[t, o], value]]]; CurrentValue[t, o]] 399 | 400 | 401 | 402 | With[{a = 2, p}, foo[]] 403 | 404 | 405 | 406 | 407 | 408 | foo[a_] := 409 | Module[{a}, 410 | (* ^ error*) 411 | a + 1 412 | ] 413 | 414 | 415 | 416 | 417 | -------------------------------------------------------------------------------- /Tests/ServerDiagnostics.mt: -------------------------------------------------------------------------------- 1 | 2 | Needs["LSPServer`"] 3 | Needs["LSPServer`ServerDiagnostics`"] 4 | 5 | 6 | Switch[$OperatingSystem, 7 | "MacOSX", 8 | wolframKernel = FileNameJoin[{$InstallationDirectory, "MacOS", "WolframKernel"}]; 9 | , 10 | "Unix", 11 | wolframKernel = FileNameJoin[{$InstallationDirectory, "Executables", "WolframKernel"}]; 12 | , 13 | "Windows", 14 | wolframKernel = FileNameJoin[{$InstallationDirectory, "WolframKernel.exe"}]; 15 | ] 16 | 17 | Test[ 18 | RunServerDiagnostic[{ 19 | wolframKernel, 20 | "-noinit", 21 | "-noprompt", 22 | "-nopaclet", 23 | "-noicon", 24 | "-nostartuppaclets", 25 | "-run", "Needs[\"LSPServer`\"];LSPServer`StartServer[]" 26 | }] 27 | , 28 | Null 29 | , 30 | TestID->"ServerDiagnostics-20220901-D3O0N3" 31 | ] 32 | -------------------------------------------------------------------------------- /Tests/TestSuite.mt: -------------------------------------------------------------------------------- 1 | 2 | TestSuite[{ 3 | "ServerDiagnostics.mt", 4 | "hover/Delayed.wlt", 5 | "hover/InFileUsage.wlt", 6 | "hover/MissingCases.wlt", 7 | "hover/Package.wlt", 8 | "hover/SystemSymbol.wlt", 9 | "hover/Values.wlt" 10 | }] 11 | -------------------------------------------------------------------------------- /Tests/hover/Delayed.wlt: -------------------------------------------------------------------------------- 1 | Get[FileNameJoin[{DirectoryName[$TestFileName], "Init.wl"}]]; 2 | initFunction[FileNameJoin[{DirectoryName[$TestFileName], "DelayedTest.wl"}]]; 3 | uri = LocalObjects`PathToURI[FileNameJoin[{DirectoryName[$TestFileName], "DelayedTest.wl"}]]; 4 | 5 | (* Function with multiple definition *) 6 | VerificationTest[ 7 | LSPServer`handleContent[ 8 | <|"method" -> "textDocument/hoverFencepost", 9 | "id" -> 1, 10 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 3, "character" -> 0|>|> 11 | |> 12 | ] 13 | , 14 | { 15 | <|"jsonrpc" -> "2.0", "id" -> 1, 16 | "result" -> <|"contents" -> <| 17 | "kind" -> "markdown", 18 | "value" -> "**Usage**\n\nf\\[x\\] has multiple definition\\.\n\n**Function Definition Patterns**\n\nf\\[x\\_\\]\n\n\n 19 | f\\[x\\_String\\]\n\n\n\nf\\[x\\_String\\] /; x\n\n\n\nf\\[x\\_Number\\]\n\n" 20 | |> 21 | |> 22 | |> 23 | }, 24 | TestID -> "IDE-Test-SetDelayed-Multiple-Definition" 25 | ] 26 | 27 | 28 | (* Hovering over q on p[q[x_]] ^:= pq[x] *) 29 | VerificationTest[ 30 | LSPServer`handleContent[ 31 | <|"method" -> "textDocument/hoverFencepost", 32 | "id" -> 2, 33 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 10, "character" -> 2|>|> 34 | |> 35 | ] 36 | , 37 | { 38 | <|"jsonrpc" -> "2.0", "id" -> 2, 39 | "result" -> <|"contents" -> <| 40 | "kind" -> "markdown", 41 | "value" -> "**Usage**\n\nNo usage message\\.\n\n**Function Definition Patterns**\n\np\\[q\\[x\\_\\]\\]\n\n" 42 | |> 43 | |> 44 | |> 45 | }, 46 | TestID -> "IDE-Test-UpSetDelayed-Definition-InnerFunction" 47 | ] 48 | 49 | 50 | (* Hovering over p where p[q[x_]] ^:= pq[x] *) 51 | VerificationTest[ 52 | LSPServer`handleContent[ 53 | <|"method" -> "textDocument/hoverFencepost", 54 | "id" -> 3, 55 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 12, "character" -> 0|>|> 56 | |> 57 | ] 58 | , 59 | { 60 | <|"jsonrpc" -> "2.0", "id" -> 3, "result" -> <|"contents" -> <|"kind" -> "markdown", "value" -> "No function information."|>|>|> 61 | }, 62 | TestID -> "IDE-Test-UpSetDelayed-Definition-Innerfunction" 63 | ] 64 | 65 | 66 | (* Hovering over g where g/:f2[g[x_]]:=f2g[x] *) 67 | VerificationTest[ 68 | LSPServer`handleContent[ 69 | <|"method" -> "textDocument/hoverFencepost", 70 | "id" -> 4, 71 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 16, "character" -> 0|>|> 72 | |> 73 | ] 74 | , 75 | { 76 | <|"jsonrpc" -> "2.0", "id" -> 4, 77 | "result" -> <|"contents" -> 78 | <|"kind" -> "markdown", 79 | "value" -> "**Usage**\n\nNo usage message\\.\n\n**Function Definition Patterns**\n\nf2\\[g\\[x\\_\\]\\]\n\n" 80 | |> 81 | |> 82 | |> 83 | }, 84 | TestID -> "IDE-Test-TagSetDelayed-Definition-TaggedFunction" 85 | ] 86 | 87 | 88 | (* Hovering over f2 where g/:f2[g[x_]]:=f2g[x] *) 89 | VerificationTest[ 90 | LSPServer`handleContent[ 91 | <|"method" -> "textDocument/hoverFencepost", 92 | "id" -> 5, 93 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 18, "character" -> 1|>|> 94 | |> 95 | ] 96 | , 97 | { 98 | <|"jsonrpc" -> "2.0", "id" -> 5, "result" -> <|"contents" -> <|"kind" -> "markdown", "value" -> "No function information."|>|>|> 99 | }, 100 | TestID -> "IDE-Test-TagSetDelayed-Definition-UntaggedFunction" 101 | ] 102 | 103 | 104 | (* Function defined with arg with a specific type *) 105 | VerificationTest[ 106 | LSPServer`handleContent[ 107 | <|"method" -> "textDocument/hoverFencepost", 108 | "id" -> 6, 109 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 25, "character" -> 3|>|> 110 | |> 111 | ] 112 | , 113 | { 114 | <|"jsonrpc" -> "2.0", "id" -> 6, 115 | "result" -> <|"contents" -> <| 116 | "kind" -> "markdown", 117 | "value" -> "**Usage**\n\nNo usage message\\.\n\n**Function Definition Patterns**\n\naddTwo\\[a\\_Integer, b\\_\\]\n\n" 118 | |> 119 | |> 120 | |> 121 | }, 122 | TestID -> "IDE-Test-SetDelayed-ConstrainedArgs" 123 | ] 124 | -------------------------------------------------------------------------------- /Tests/hover/DelayedTest.wl: -------------------------------------------------------------------------------- 1 | f::usage = "f[x] has multiple definition."; 2 | 3 | 4 | f[x_] := ppp[x] /; x > 0; 5 | f[x_String]:=x <> "Test"; 6 | f[x_String]:=x <> "Test"; 7 | f[x_String]/;x :=x <> "Test-String"; 8 | f[x_Number] := x; 9 | 10 | (* UpSetDelayed *) 11 | p[q[x_]] ^:= pq[x] 12 | 13 | p[q[2]] 14 | (* TagSetDelayed *) 15 | g/:f2[g[x_]]:=f2g[x] 16 | 17 | g[x] 18 | 19 | f2[] 20 | 21 | g/:f2[g[x_]]:=f2g[x] 22 | 23 | (* Function with constrained args *) 24 | addTwo[a_Integer,b_] := (a+b); 25 | 26 | addTwo; -------------------------------------------------------------------------------- /Tests/hover/InFileUsage.wlt: -------------------------------------------------------------------------------- 1 | Get[FileNameJoin[{DirectoryName[$TestFileName], "Init.wl"}]]; 2 | initFunction[FileNameJoin[{DirectoryName[$TestFileName], "InFileUsageTest.wl"}]]; 3 | uri = LocalObjects`PathToURI[FileNameJoin[{DirectoryName[$TestFileName], "InFileUsageTest.wl"}]]; 4 | 5 | (* Function with simple usage message *) 6 | VerificationTest[ 7 | LSPServer`handleContent[ 8 | <|"method" -> "textDocument/hoverFencepost", 9 | "id" -> 1, 10 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 5, "character" -> 5|>|> 11 | |> 12 | ] 13 | , 14 | { 15 | <|"jsonrpc" -> "2.0", "id" -> 1, 16 | "result" -> <|"contents" -> <| 17 | "kind" -> "markdown", 18 | "value" -> "**Usage**\n\nmySin is just a sin function\\.\n\n**Function Definition Patterns**\n\nmySin\\[x\\_\\]\n\n" 19 | |> 20 | |> 21 | |> 22 | }, 23 | TestID -> "IDE-Test-InFileUsage-Simple" 24 | ] 25 | 26 | 27 | (* A function with usage message one tab space away from start of line *) 28 | VerificationTest[ 29 | LSPServer`handleContent[ 30 | <|"method" -> "textDocument/hoverFencepost", 31 | "id" -> 2, 32 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 9, "character" -> 8|>|> 33 | |> 34 | ] 35 | , 36 | { 37 | <|"jsonrpc" -> "2.0", "id" -> 2, 38 | "result" -> <|"contents" -> <| 39 | "kind" -> "markdown", 40 | "value" -> "**Usage**\n\nmyTan is Tan function\\.\n\n**Function Definition Patterns**\n\nmyTan\\[x\\_\\]\n\n" 41 | |> 42 | |> 43 | |> 44 | }, 45 | TestID -> "IDE-Test-InFileUsage-Tab" 46 | ] 47 | 48 | 49 | (* Function with usage message containing linear syntax *) 50 | VerificationTest[ 51 | LSPServer`handleContent[ 52 | <|"method" -> "textDocument/hoverFencepost", 53 | "id" -> 3, 54 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 7, "character" -> 3|>|> 55 | |> 56 | ] 57 | , 58 | { 59 | <|"jsonrpc" -> "2.0", "id" -> 3, 60 | "result" -> <|"contents" -> <| 61 | "kind" -> "markdown", 62 | "value" -> "**Usage**\n\nCos\\[**z**\\] gives the cosine of *z*\\. \n\n**Function Definition Patterns**\n\nmyCos\\[x\\_\\]\n\n" 63 | |> 64 | |> 65 | |> 66 | }, 67 | TestID -> "IDE-Test-InFileUsage-LinearSyntax" 68 | ] 69 | 70 | 71 | (* Usage message defined with SetDelay *) 72 | VerificationTest[ 73 | LSPServer`handleContent[ 74 | <|"method" -> "textDocument/hoverFencepost", 75 | "id" -> 4, 76 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 18, "character" -> 1|>|> 77 | |> 78 | ] 79 | , 80 | { 81 | <|"jsonrpc" -> "2.0", "id" -> 4, 82 | "result" -> <|"contents" -> <| 83 | "kind" -> "markdown", 84 | "value" -> "**Usage**\n\nUsage with SetDelay\n\n**Function Definition Patterns**\n\naa\\[x\\_\\]\n\n" 85 | |> 86 | |> 87 | |> 88 | }, 89 | TestID -> "IDE-Test-Usage-with-SetDelay" 90 | ] 91 | 92 | 93 | (* Function without usage tag *) 94 | VerificationTest[ 95 | LSPServer`handleContent[<|"method" -> "textDocument/hoverFencepost", 96 | "id" -> 5, 97 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 23, "character" -> 2|>|> 98 | |> 99 | ], 100 | { 101 | <|"jsonrpc" -> "2.0", "id" -> 5, 102 | "result" -> <|"contents" -> <| 103 | "kind" -> "markdown", 104 | "value" -> "No function information." 105 | |> 106 | |> 107 | |> 108 | }, 109 | TestID -> "IDE-Test-No-Usage-Tag" 110 | ] 111 | -------------------------------------------------------------------------------- /Tests/hover/InFileUsageTest.wl: -------------------------------------------------------------------------------- 1 | mySin::usage = "mySin is just a sin function." 2 | myTan::usage = "myTan is Tan function." 3 | myCos::usage = "\!\(\*RowBox[{\"Cos\", \"[\", StyleBox[\"z\", Bold], \"]\"}]\) gives the cosine of \!\(\*StyleBox[\"z\", \"TI\"]\). " 4 | 5 | 6 | mySin[x_]:= Sin[x]; 7 | 8 | myCos[x_]:= Cos[x]; 9 | 10 | myTan[x_]:= Tan[x]; 11 | 12 | 13 | 14 | a::usage = "Test for single usage." 15 | a[]:=2; 16 | a[] 17 | 18 | 19 | aa::usage := "Usage with SetDelay"; 20 | aa[x_] := 2x; 21 | aa[2] 22 | 23 | 24 | aaa::foo = "test" 25 | aaa[] 26 | 27 | aaaa::foo := "test" 28 | aaaa[] 29 | 30 | 31 | bb::usage::English = "test" 32 | bb[x_] := 1+2 33 | bb[] 34 | 35 | 36 | bbb::usage::English := "test" 37 | bbb[x_] := 1+2 38 | bbb[] 39 | 40 | 41 | f::usage = "Usage 1."; 42 | f::usage := "Usage 2."; 43 | f::usage::English = "Usage 3" 44 | f::usage::English := "Usage 4." 45 | 46 | f[x_] := ppp[x] /; x > 0; 47 | f[x_String]:=x <> "Test"; 48 | f[x_String]:=x <> "Test"; 49 | f[x_String]/;x :=x <> "Test-String"; 50 | f[x_Number] := x; 51 | f[] = 42; 52 | 53 | linearSyntaxTest::usage = "\!\(\*RowBox[{\"Sin\", \"[\", StyleBox[\"z\", Bold], \"]\"}]\) gives the sine of \!\(\*StyleBox[\"z\", \"TI\"]\). " 54 | linearSyntaxTest[x_]:= 2x; -------------------------------------------------------------------------------- /Tests/hover/Init.wl: -------------------------------------------------------------------------------- 1 | << LSPServer` 2 | 3 | initFunction[filePath_]:= 4 | Module[{uri, fileText}, 5 | uri = LocalObjects`PathToURI[filePath]; 6 | fileText = ReadString[filePath]; 7 | 8 | LSPServer`handleContent[<| 9 | "method"-> #, 10 | "params"-> <| 11 | "textDocument"-> <| 12 | "uri" -> uri, 13 | "languageId"->"wolfram", 14 | "version"-> 1, 15 | "text"-> fileText 16 | |> 17 | |> 18 | |>]& /@ 19 | { 20 | "textDocument/didOpenFencepost", 21 | "textDocument/concreteParse", 22 | "textDocument/concreteTabsParse", 23 | "textDocument/aggregateParse", 24 | "textDocument/aggregateTabsParse", 25 | "textDocument/abstractParse" 26 | } 27 | ] -------------------------------------------------------------------------------- /Tests/hover/MissingCases.wlt: -------------------------------------------------------------------------------- 1 | Get[FileNameJoin[{DirectoryName[$TestFileName], "Init.wl"}]]; 2 | initFunction[FileNameJoin[{DirectoryName[$TestFileName], "MissingCasesTest.wl"}]]; 3 | uri = LocalObjects`PathToURI[FileNameJoin[{DirectoryName[$TestFileName], "MissingCasesTest.wl"}]]; 4 | 5 | (* Function is defined with no usage message *) 6 | VerificationTest[ 7 | LSPServer`handleContent[ 8 | <|"method" -> "textDocument/hoverFencepost", 9 | "id" -> 1, 10 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 5, "character" -> 11|>|> 11 | |> 12 | ] 13 | , 14 | { 15 | <|"jsonrpc" -> "2.0", "id" -> 1, 16 | "result" -> <|"contents" -> <| 17 | "kind" -> "markdown", 18 | "value" -> "**Usage**\n\nNo usage message\\.\n\n**Function Definition Patterns**\n\nnoFunctionUsage\\[x\\_Integer\\]\n\n" 19 | |> 20 | |> 21 | |> 22 | }, 23 | TestID -> "IDE-Test-NoUsage" 24 | ] 25 | 26 | 27 | (* Function is not defined but only has usage message *) 28 | VerificationTest[ 29 | LSPServer`handleContent[ 30 | <|"method" -> "textDocument/hoverFencepost", "id" -> 2, 31 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 8, "character" -> 5|>|> 32 | |> 33 | ] 34 | , 35 | { 36 | <|"jsonrpc" -> "2.0", "id" -> 2, 37 | "result" -> <|"contents" -> <| 38 | "kind" -> "markdown", 39 | "value" -> "**Usage**\n\nTest function with only usage\\.\n\n**Function Definition Patterns**\n\nNo function defined\\." 40 | |> 41 | |> 42 | |> 43 | }, 44 | TestID -> "IDE-Test-OnlyUsage"] 45 | 46 | 47 | (* Function is not defined and has usage message *) 48 | VerificationTest[ 49 | LSPServer`handleContent[ 50 | <|"method" -> "textDocument/hoverFencepost", "id" -> 3, 51 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 11, "character" -> 8|>|> 52 | |> 53 | ] 54 | , 55 | { 56 | <|"jsonrpc" -> "2.0", "id" -> 3, 57 | "result" -> <|"contents" -> <| 58 | "kind" -> "markdown", 59 | "value" -> "No function information." 60 | |> 61 | |> 62 | |> 63 | }, 64 | TestID -> "IDE-Test-NoFunction-Information"] 65 | 66 | -------------------------------------------------------------------------------- /Tests/hover/MissingCasesTest.wl: -------------------------------------------------------------------------------- 1 | onlyUsage::usage = "Test function with only usage." 2 | 3 | (* Function is defined with no usage message *) 4 | noFunctionUsage[x_Integer] := x^2; 5 | 6 | noFunctionUsage[x]; 7 | 8 | (* Function is not defined, but only has usage message *) 9 | onlyUsage[]; 10 | 11 | (* Function is not defined and has usage message *) 12 | noFunction[x]; -------------------------------------------------------------------------------- /Tests/hover/Package.wlt: -------------------------------------------------------------------------------- 1 | Get[FileNameJoin[{DirectoryName[$TestFileName], "Init.wl"}]]; 2 | initFunction[FileNameJoin[{DirectoryName[$TestFileName], "PackageTest.wl"}]]; 3 | uri = LocalObjects`PathToURI[FileNameJoin[{DirectoryName[$TestFileName], "PackageTest.wl"}]]; 4 | 5 | (* SystemSymbol in a package *) 6 | VerificationTest[ 7 | LSPServer`handleContent[ 8 | <|"method" -> "textDocument/hoverFencepost", "id" -> 1, 9 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 25, "character" -> 2|>|> 10 | |> 11 | ] 12 | , 13 | { 14 | <|"jsonrpc" -> "2.0", "id" -> 1, 15 | "result" -> <|"contents" -> <| 16 | "kind" -> "markdown", 17 | "value" -> "Plot\\[*f*,\\{*x*,*x*\\_*min*,*x*\\_*max*\\}\\] generates a plot of \ 18 | *f* as a function of *x* from *x*\\_*min* to *x*\\_*max*\\. \n\n\ 19 | Plot\\[\\{*f*\\_1,*f*\\_2,\[Ellipsis]\\},\\{*x*,*x*\\_*min*,*x*\\_*\ 20 | max*\\}\\] plots several functions *f*\\_*i*\\. \n\nPlot\\[\\{\ 21 | \[Ellipsis],*w*\\[*f*\\_*i*\\],\[Ellipsis]\\},\[Ellipsis]\\] plots \ 22 | *f*\\_*i* with features defined by the symbolic wrapper *w*\\.\n\n\ 23 | Plot\\[\[Ellipsis],\\{*x*\\}\[Element]*reg*\\] takes the variable *x* \ 24 | to be in the geometric region *reg*\\.\n\n_[Plot: Web \ 25 | Documentation](https://reference.wolfram.com/language/ref/Plot.html)_" 26 | |> 27 | |> 28 | |> 29 | }, 30 | TestID -> "IDE-Test-SystemSymbol-In-Package" 31 | ] 32 | 33 | 34 | (* Function is defined in the Private context in a package*) 35 | VerificationTest[ 36 | LSPServer`handleContent[ 37 | <|"method" -> "textDocument/hoverFencepost", "id" -> 2, 38 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 11, "character" -> 14|>|> 39 | |> 40 | ] 41 | , 42 | { 43 | <| 44 | "jsonrpc" -> "2.0", "id" -> 2, 45 | "result" -> <|"contents" -> <| 46 | "kind" -> "markdown", 47 | "value" -> "**Usage**\n\nUsage message of testFunction\\.\n\n**Function Definition Patterns**\n\ntestFunction\\[a\\_\\]\\[b\\_\\]\n\n" 48 | |> 49 | |> 50 | |> 51 | }, 52 | TestID -> "IDE-Test-ContextDepth-1-In-Package" 53 | ] 54 | 55 | 56 | (* 57 | If a function is defined one level below the Private context level, 58 | we are not going to detect that. 59 | *) 60 | VerificationTest[ 61 | LSPServer`handleContent[ 62 | <|"method" -> "textDocument/hoverFencepost", "id" -> 3, 63 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 16, "character" -> 18|>|> 64 | |> 65 | ] 66 | , 67 | { 68 | <|"jsonrpc" -> "2.0", "id" -> 3, 69 | "result" -> <|"contents" -> <| 70 | "kind" -> "markdown", 71 | "value" -> "**Usage**\n\nUsage message of testFunction\\.\n\n**Function Definition Patterns**\n\ntestFunction\\[a\\_\\]\\[b\\_\\]\n\n" 72 | |> 73 | |> 74 | |> 75 | }, 76 | TestID -> "IDE-Test-ContextDepth-2-In-Package" 77 | ] 78 | 79 | 80 | (* Function with multiple usage in a package, defined with Set and SetDelayed *) 81 | VerificationTest[ 82 | LSPServer`handleContent[ 83 | <|"method" -> "textDocument/hoverFencepost", "id" -> 4, 84 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 35, "character" -> 15|>|> 85 | |> 86 | ] 87 | , 88 | { 89 | <|"jsonrpc" -> "2.0", "id" -> 4, 90 | "result" -> <|"contents" -> <| 91 | "kind" -> "markdown", 92 | "value" -> "**Usage**\n\nFirst usage message of multiUsageFunction\\.\n\nSecond usage message of multiUsageFunction\\.\n\n**Function Definition Patterns**\n\nmultiUsageFunction\\[x\\_\\]\n\n" 93 | |> 94 | |> 95 | |> 96 | }, 97 | TestID -> "IDE-Test-MultipleUsage-In-Package" 98 | ] 99 | 100 | 101 | (* Function defined one tabspace away from the start of the line inside a package *) 102 | VerificationTest[ 103 | LSPServer`handleContent[ 104 | <|"method" -> "textDocument/hoverFencepost", "id" -> 5, 105 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 31, "character" -> 5|>|> 106 | |> 107 | ] 108 | , 109 | { 110 | <|"jsonrpc" -> "2.0", "id" -> 5, 111 | "result" -> <|"contents" -> <| 112 | "kind" -> "markdown", 113 | "value" -> "**Usage**\n\nNo usage message\\.\n\n**Function Definition Patterns**\n\nfoo\\[\\]\n\n" 114 | |> 115 | |> 116 | |> 117 | }, 118 | TestID -> "IDE-Test-Tab-Function-In-Package" 119 | ] 120 | 121 | 122 | (* A function is defined with UpSetDelayed inside a package *) 123 | VerificationTest[ 124 | LSPServer`handleContent[ 125 | <|"method" -> "textDocument/hoverFencepost", "id" -> 6, 126 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 23, "character" -> 2|>|> 127 | |> 128 | ] 129 | , 130 | { 131 | <|"jsonrpc" -> "2.0", "id" -> 6, 132 | "result" -> <|"contents" -> <| 133 | "kind" -> "markdown", 134 | "value" -> "**Usage**\n\nNo usage message\\.\n\n**Function Definition Patterns**\n\nf\\[g\\[x\\_\\]\\]\n\n" 135 | |> 136 | |> 137 | |> 138 | }, 139 | TestID -> "IDE-Test-UpSetDelayed-Function-1-In-Package" 140 | ] 141 | 142 | 143 | (* 144 | If a function is defined with TagSetDelayed, 145 | hovering over the untagged function "f" will show "No function information." 146 | *) 147 | VerificationTest[ 148 | LSPServer`handleContent[ 149 | <|"method" -> "textDocument/hoverFencepost", "id" -> 7, 150 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 27, "character" -> 5|>|> 151 | |> 152 | ] 153 | , 154 | { 155 | <|"jsonrpc" -> "2.0", "id" -> 7, 156 | "result" -> <|"contents" -> <| 157 | "kind" -> "markdown", 158 | "value" -> "No function information." 159 | |> 160 | |> 161 | |> 162 | }, 163 | TestID -> "IDE-Test-TagSetDelayed-Function-2-In-Package" 164 | ] 165 | -------------------------------------------------------------------------------- /Tests/hover/PackageTest.wl: -------------------------------------------------------------------------------- 1 | (* ::Package:: *) 2 | 3 | BeginPackage["MyPackages`Test`"] 4 | 5 | testFunction::usage = "Usage message of testFunction." ; 6 | multiUsageFunction::usage = "First usage message of multiUsageFunction." ; 7 | multiUsageFunction::usage := "Second usage message of multiUsageFunction." ; 8 | 9 | Begin["`Private`"] 10 | 11 | testFunction [a_] [b_] := (a+b)/2; 12 | p = testFunction[3][4]; 13 | 14 | (* We are not going to search one level down *) 15 | Begin["`Context1`"] 16 | 17 | testFunction [a_] := (a)/2; 18 | 19 | End[] 20 | 21 | (* Function with UpSetDelayed *) 22 | f[g[x_]] ^:= fg[x] 23 | 24 | f[g[2]] 25 | 26 | Plot[Sin[x],{x, 0, Pi}] 27 | (* Function with TagSetDelayed *) 28 | g /: f[g[x_]] := fg[x]; 29 | 30 | f[g[2]] 31 | 32 | foo[] := xxx 33 | 34 | foo[] 35 | 36 | multiUsageFunction[x_] := x+1; 37 | 38 | 39 | End[] 40 | EndPackage[] -------------------------------------------------------------------------------- /Tests/hover/SystemSymbol.wlt: -------------------------------------------------------------------------------- 1 | Get[FileNameJoin[{DirectoryName[$TestFileName], "Init.wl"}]]; 2 | initFunction[FileNameJoin[{DirectoryName[$TestFileName], "SystemSymbolTest.wl"}]]; 3 | uri = LocalObjects`PathToURI[FileNameJoin[{DirectoryName[$TestFileName], "SystemSymbolTest.wl"}]]; 4 | 5 | (* SystemSymbol containing usage message with linear syntax *) 6 | VerificationTest[ 7 | LSPServer`handleContent[ 8 | <|"method" -> "textDocument/hoverFencepost", 9 | "id" -> 1, 10 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 0, "character" -> 1|>|> 11 | |> 12 | ] 13 | , 14 | { 15 | <|"jsonrpc" -> "2.0", "id" -> 1, 16 | "result" -> <|"contents" -> <| 17 | "kind" -> "markdown", 18 | "value" -> "Sin\\[*z*\\] gives the sine of *z*\\. \n\n_[Sin: Web Documentation](https://reference.wolfram.com/language/ref/Sin.html)_" 19 | |> 20 | |> 21 | |> 22 | }, 23 | TestID -> "IDE-Test-SystemSymbol-Linear-Syntax" 24 | ] 25 | 26 | 27 | (* SystemSymbol (cos) is located one tabspace away from start of line in SystemSymbolTest.wl *) 28 | VerificationTest[ 29 | LSPServer`handleContent[ 30 | <|"method" -> "textDocument/hoverFencepost", 31 | "id" -> 2, 32 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 2, "character" -> 6|>|> 33 | |> 34 | ], 35 | { 36 | <|"jsonrpc" -> "2.0", "id" -> 2, 37 | "result" -> <|"contents" -> <| 38 | "kind" -> "markdown", 39 | "value" -> "Cos\\[*z*\\] gives the cosine of *z*\\. \n\n_[Cos: Web Documentation](https://reference.wolfram.com/language/ref/Cos.html)_" 40 | |> 41 | |> 42 | |> 43 | }, 44 | TestID -> "IDE-Test-SystemSymbol-withTab"] 45 | 46 | 47 | (* SystemSymbol (ExternalEvaluate) with multi-line usage message *) 48 | VerificationTest[ 49 | LSPServer`handleContent[ 50 | <|"method" -> "textDocument/hoverFencepost", 51 | "id" -> 3, 52 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 4, "character" -> 11|>|> 53 | |> 54 | ], 55 | { 56 | <|"jsonrpc" -> "2.0", "id" -> 3, 57 | "result" -> <|"contents" -> 58 | <|"kind" -> "markdown", 59 | "value" -> "ExternalEvaluate\\[\"*sys*\",\"*cmd*\"\\] evaluates the command \ 60 | *cmd* in the external evaluator *sys*, returning an expression corresponding \ 61 | to the output\\.\n\nExternalEvaluate\\[\\{\"*sys*\",*opts*\\},\"*cmd*\"\\] \ 62 | uses the options *opts* for the external evaluator\\.\n\nExternalEvaluate\\[DatabaseReference\\[*ref*\\],\"*cmd*\"\\] \ 63 | evaluates *cmd* using the database specified by *ref*\\.\n\nExternalEvaluate\\[*assoc*,\"*cmd*\"\\] evaluates *cmd* \ 64 | using the external evaluator specified by *assoc*\\.\n\nExternalEvaluate\\[*session*,\"*cmd*\"\\] evaluates *cmd* \ 65 | in the specified running ExternalSessionObject\\.\n\nExternalEvaluate\\[*sys*\\->\"*type*\",\[Ellipsis]\\] \ 66 | returns output converted to the specified type\\. \n\nExternalEvaluate\\[*spec*,*obj*\\] evaluates the content \ 67 | of the specified File, URL or CloudObject\\.\n\nExternalEvaluate\\[*spec*,*assoc*\\] evaluates the command specified \ 68 | by *assoc*\\.\n\nExternalEvaluate\\[*spec*,\\{*cmd*\\_1,*cmd*\\_2,\[Ellipsis]\\}\\] evaluates the list of \ 69 | commands *cmd*\\_*i*\\.\n\nExternalEvaluate\\[*spec*\\] represents an operator form of ExternalEvaluate that \ 70 | can be applied to a command or object\\.\n\nEXPERIMENTAL\n\n_[ExternalEvaluate: Web Documentation](https://reference.wolfram.com/language/ref/ExternalEvaluate.html)_" 71 | |> 72 | |> 73 | |> 74 | }, 75 | TestID -> "IDE-Test-SystemSymbol-Multiline-Usage" 76 | ] 77 | 78 | 79 | -------------------------------------------------------------------------------- /Tests/hover/SystemSymbolTest.wl: -------------------------------------------------------------------------------- 1 | Sin[x] 2 | 3 | Cos[x] 4 | 5 | ExternalEvaluate[] -------------------------------------------------------------------------------- /Tests/hover/Values.wlt: -------------------------------------------------------------------------------- 1 | Get[FileNameJoin[{DirectoryName[$TestFileName], "Init.wl"}]]; 2 | initFunction[FileNameJoin[{DirectoryName[$TestFileName], "ValuesTest.wl"}]]; 3 | uri = LocalObjects`PathToURI[FileNameJoin[{DirectoryName[$TestFileName], "ValuesTest.wl"}]]; 4 | 5 | (* Function with SubValues *) 6 | VerificationTest[ 7 | LSPServer`handleContent[ 8 | <|"method" -> "textDocument/hoverFencepost", 9 | "id" -> 1, 10 | "params" -> <|"textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 4, "character" -> 2|>|> 11 | |> 12 | ] 13 | , 14 | { 15 | <|"jsonrpc" -> "2.0", "id" -> 1, 16 | "result" -> <|"contents" -> <| 17 | "kind" -> "markdown", 18 | "value" -> "**Usage**\n\nNo usage message\\.\n\n**Function Definition Patterns**\n\nsubFn\\[a\\_\\]\\[b\\_\\]\n\n" 19 | |> 20 | |> 21 | |> 22 | }, 23 | TestID -> "IDE-Test-SubValues-Simple" 24 | ] 25 | 26 | 27 | (* A Function with SubValues and usage message*) 28 | VerificationTest[ 29 | LSPServer`handleContent[ 30 | <|"method" -> "textDocument/hoverFencepost", 31 | "id" -> 2, 32 | "params" -> <| 33 | "textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 8, "character" -> 7 34 | |> 35 | |> 36 | |> 37 | ] 38 | , 39 | { 40 | <|"jsonrpc" -> "2.0", "id" -> 2, 41 | "result" -> <|"contents" -> <| 42 | "kind" -> "markdown", 43 | "value" -> "**Usage**\n\nSubValue function with usage\\.\n\n**Function Definition Patterns**\n\nsubFnUsage\\[a\\_\\]\\[b\\_\\]\n\n" 44 | |> 45 | |> 46 | |> 47 | }, 48 | TestID -> "IDE-Test-SubValues-Usage" 49 | ] 50 | 51 | 52 | (* A Function with UpValue and usage message*) 53 | VerificationTest[ 54 | LSPServer`handleContent[ 55 | <|"method" -> "textDocument/hoverFencepost", 56 | "id" -> 3, 57 | "params" -> <| 58 | "textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 12, "character" -> 7 59 | |> 60 | |> 61 | |> 62 | ] 63 | , 64 | { 65 | <|"jsonrpc" -> "2.0", "id" -> 3, 66 | "result" -> <|"contents" -> <| 67 | "kind" -> "markdown", 68 | "value" -> "**Usage**\n\ng is a function with UpValue\\.\n\n**Function Definition Patterns**\n\nf\\[g\\]\n\n\n\nf\\[g\\[x\\_\\]\\]\n\n" 69 | |> 70 | |> 71 | |> 72 | }, 73 | TestID -> "IDE-Test-UpValues-Usage" 74 | ] 75 | 76 | 77 | (* A Function with UpValue and usage message*) 78 | VerificationTest[ 79 | LSPServer`handleContent[ 80 | <|"method" -> "textDocument/hoverFencepost", 81 | "id" -> 4, 82 | "params" -> <| 83 | "textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 13, "character" -> 0 84 | |> 85 | |> 86 | |> 87 | ] 88 | , 89 | { 90 | <|"jsonrpc" -> "2.0", "id" -> 4, 91 | "result" -> <|"contents" -> <| 92 | "kind" -> "markdown", 93 | "value" -> "No function information." 94 | |> 95 | |> 96 | |> 97 | }, 98 | TestID -> "IDE-Test-UpValues-Untagged" 99 | ] 100 | 101 | 102 | (* A Function with DownValue and usage message*) 103 | VerificationTest[ 104 | LSPServer`handleContent[ 105 | <|"method" -> "textDocument/hoverFencepost", 106 | "id" -> 5, 107 | "params" -> <| 108 | "textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 17, "character" -> 6 109 | |> 110 | |> 111 | |> 112 | ] 113 | , 114 | { 115 | <|"jsonrpc" -> "2.0", "id" -> 5, 116 | "result" -> <|"contents" -> <| 117 | "kind" -> "markdown", 118 | "value" -> "**Usage**\n\ndownValFn is a function with DownValue\\.\n\n**Function Definition Patterns**\n\ndownValFn\\[x\\_ /; x > \\-2\\]\n\n\n\ndownValFn\\[x\\_ /; x < 2\\]\n\n" 119 | |> 120 | |> 121 | |> 122 | }, 123 | TestID -> "IDE-Test-DownValues-Usage" 124 | ] 125 | 126 | 127 | (* 128 | A Function with DownValue and usage message 129 | downValFn[x_ /; x < 2] := g2[x] 130 | hover over g2 131 | *) 132 | VerificationTest[ 133 | LSPServer`handleContent[ 134 | <|"method" -> "textDocument/hoverFencepost", 135 | "id" -> 6, 136 | "params" -> <| 137 | "textDocument" -> <|"uri" -> uri|>, "position" -> <|"line" -> 18, "character" -> 27 138 | |> 139 | |> 140 | |> 141 | ] 142 | , 143 | { 144 | <|"jsonrpc" -> "2.0", "id" -> 6, 145 | "result" -> <|"contents" -> <| 146 | "kind" -> "markdown", 147 | "value" -> "No function information." 148 | |> 149 | |> 150 | |> 151 | }, 152 | TestID -> "IDE-Test-DownValues-Untagged" 153 | ] 154 | -------------------------------------------------------------------------------- /Tests/hover/ValuesTest.wl: -------------------------------------------------------------------------------- 1 | subFnUsage::usage = "SubValue function with usage." 2 | 3 | subFn[a_][b_] := a+b; 4 | 5 | subFn[3][4] 6 | 7 | subFnUsage[a_][b_] := a+b+2; 8 | 9 | subFnUsage[3][4] 10 | 11 | g::usage = "g is a function with UpValue." 12 | 13 | g /: f[g[x_]] := h[x] 14 | f[g] ^:= h[1] 15 | g[5] 16 | 17 | downValFn::usage = "downValFn is a function with DownValue." 18 | downValFn[x_ /; x > -2] := g1[x] 19 | downValFn[x_ /; x < 2] := g2[x] 20 | downValFn[0] -------------------------------------------------------------------------------- /Tests/outline/test.wl: -------------------------------------------------------------------------------- 1 | (* ::Section:: *) 2 | (* Section 1 *) 3 | 4 | $a = 1 5 | 6 | (* ::Subsection:: *) 7 | (* Subsection 1 *) 8 | 9 | $b = 2 10 | 11 | (* ::Subsubsection:: *) 12 | (* Subsubsection 1 *) 13 | 14 | $c = 3 15 | 16 | (* ::Subsubsection:: *) 17 | (* Subsubsection 2 *) 18 | 19 | $d = 4 20 | 21 | (* ::Subsection:: *) 22 | (* Subsection 2 *) 23 | 24 | $e = 5 25 | 26 | (* ::Subsubsection:: *) 27 | (* Subsubsection 3 *) 28 | 29 | $f = 6 30 | 31 | (* ::Section:: *) 32 | (* Section 2 *) 33 | 34 | $g = 7 35 | 36 | (* ::Subsection:: *) 37 | (* Subsection 3 *) 38 | 39 | $h = 8 -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/BadSymbols.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"AllFalse", "AnyFalse", "Boolean", "Cloudbase", "ComplexQ", "DataSet", 5 | "ExpandFilename", "ExportPacket", "Failed", "FalseQ", 6 | "InterpolationFunction", "InterpolationPolynomial", "Match", 7 | "OptionPattern", "OptionsQ", "RationalQ", "RealQ", "StringMatch", "SymbolQ", 8 | "UnSameQ", "UrlExecute", "$PathNameSeparator", "$RegisteredUsername"} 9 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/Constants.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"Constant", "Flat", "HoldAll", "HoldAllComplete", "HoldFirst", "HoldRest", 5 | "Listable", "Locked", "NHoldAll", "NHoldFirst", "NHoldRest", 6 | "NumericFunction", "OneIdentity", "Orderless", "Protected", "ReadProtected", 7 | "SequenceHold", "Stub", "Temporary", "Above", "After", "Algebraics", "All", 8 | "Anonymous", "Automatic", "Axis", "Back", "Backward", "Baseline", "Before", 9 | "Below", "Black", "Blue", "Bold", "Booleans", "Bottom", "Boxes", "Brown", 10 | "Byte", "Catalan", "CellStyle", "Center", "Character", "Complexes", 11 | "ComplexInfinity", "Cyan", "Dashed", "DefaultAxesStyle", "DefaultBaseStyle", 12 | "DefaultBoxStyle", "DefaultFaceGridsStyle", "DefaultFieldHintStyle", 13 | "DefaultFrameStyle", "DefaultFrameTicksStyle", "DefaultGridLinesStyle", 14 | "DefaultLabelStyle", "DefaultMenuStyle", "DefaultTicksStyle", 15 | "DefaultTooltipStyle", "Degree", "Delimiter", "DigitCharacter", "DotDashed", 16 | "Dotted", "E", "EndOfBuffer", "EndOfFile", "EndOfLine", "EndOfString", 17 | "EulerGamma", "Expression", "False", "FontProperties", "Forward", 18 | "ForwardBackward", "Friday", "Front", "FrontEndDynamicExpression", "Full", 19 | "General", "Glaisher", "GoldenAngle", "GoldenRatio", "Gray", "Green", 20 | "Here", "HexadecimalCharacter", "I", "Indeterminate", "Infinity", 21 | "Inherited", "Integer", "Integers", "Italic", "Khinchin", "Large", "Larger", 22 | "Left", "LetterCharacter", "LightBlue", "LightBrown", "LightCyan", 23 | "LightGray", "LightGreen", "LightMagenta", "LightOrange", "LightPink", 24 | "LightPurple", "LightRed", "LightYellow", "MachinePrecision", "Magenta", 25 | "Manual", "Medium", "MeshCellCentroid", "MeshCellMeasure", 26 | "MeshCellQuality", "Monday", "NegativeIntegers", "NegativeRationals", 27 | "NegativeReals", "None", "NonNegativeIntegers", "NonNegativeRationals", 28 | "NonNegativeReals", "NonPositiveIntegers", "NonPositiveRationals", 29 | "NonPositiveReals", "Now", "NoWhitespace", "Null", "Number", "NumberString", 30 | "Orange", "Pi", "Pink", "Plain", "PositiveIntegers", "PositiveRationals", 31 | "PositiveReals", "Primes", "PunctuationCharacter", "Purple", "Rationals", 32 | "Real", "Reals", "Record", "Red", "Right", "Saturday", "Small", "Smaller", 33 | "SpanFromAbove", "SpanFromBoth", "SpanFromLeft", "StartOfLine", 34 | "StartOfString", "String", "Struckthrough", "Sunday", "Thick", "Thin", 35 | "Thursday", "Tiny", "Today", "Tomorrow", "Top", "Transparent", "True", 36 | "Tuesday", "Undefined", "Underlined", "Wednesday", "White", "Whitespace", 37 | "WhitespaceCharacter", "Word", "WordBoundary", "WordCharacter", "Yellow", 38 | "Yesterday", "$Aborted", "$ActivationKey", "$AllowDataUpdates", 39 | "$AllowInternet", "$AssertFunction", "$Assumptions", "$AudioInputDevices", 40 | "$AudioOutputDevices", "$BaseDirectory", "$BasePacletsDirectory", 41 | "$BatchInput", "$BatchOutput", "$ByteOrdering", "$CacheBaseDirectory", 42 | "$Canceled", "$CharacterEncoding", "$CharacterEncodings", 43 | "$CloudAccountName", "$CloudBase", "$CloudConnected", 44 | "$CloudCreditsAvailable", "$CloudEvaluation", "$CloudExpressionBase", 45 | "$CloudObjectNameFormat", "$CloudObjectURLType", "$CloudRootDirectory", 46 | "$CloudSymbolBase", "$CloudUserID", "$CloudUserUUID", "$CloudVersion", 47 | "$CommandLine", "$CompilationTarget", "$Context", "$ContextAliases", 48 | "$ContextPath", "$ControlActiveSetting", "$Cookies", "$CreationDate", 49 | "$CurrentLink", "$CurrentTask", "$DateStringFormat", 50 | "$DefaultAudioInputDevice", "$DefaultAudioOutputDevice", "$DefaultFrontEnd", 51 | "$DefaultImagingDevice", "$DefaultKernels", "$DefaultLocalBase", 52 | "$DefaultLocalKernel", "$Display", "$DisplayFunction", 53 | "$DistributedContexts", "$DynamicEvaluation", "$Echo", 54 | "$EmbedCodeEnvironments", "$EmbeddableServices", "$Epilog", 55 | "$EvaluationCloudBase", "$EvaluationCloudObject", "$EvaluationEnvironment", 56 | "$ExportFormats", "$Failed", "$FontFamilies", "$FrontEnd", 57 | "$FrontEndSession", "$GeoLocation", "$GeoLocationCity", 58 | "$GeoLocationCountry", "$GeoLocationSource", "$HomeDirectory", "$IgnoreEOF", 59 | "$ImageFormattingWidth", "$ImageResolution", "$ImagingDevice", 60 | "$ImagingDevices", "$ImportFormats", "$InitialDirectory", "$Input", 61 | "$InputFileName", "$InputStreamMethods", "$Inspector", 62 | "$InstallationDirectory", "$InterpreterTypes", "$IterationLimit", 63 | "$KernelCount", "$KernelID", "$Language", "$LibraryPath", 64 | "$LicenseExpirationDate", "$LicenseID", "$LicenseServer", "$Linked", 65 | "$LocalBase", "$LocalSymbolBase", "$MachineAddresses", "$MachineDomains", 66 | "$MachineEpsilon", "$MachineID", "$MachineName", "$MachinePrecision", 67 | "$MachineType", "$MaxExtraPrecision", "$MaxMachineNumber", "$MaxNumber", 68 | "$MaxPiecewiseCases", "$MaxPrecision", "$MaxRootDegree", "$MessageGroups", 69 | "$MessageList", "$MessagePrePrint", "$Messages", "$MinMachineNumber", 70 | "$MinNumber", "$MinPrecision", "$MobilePhone", "$ModuleNumber", 71 | "$NetworkConnected", "$NewMessage", "$NewSymbol", 72 | "$NotebookInlineStorageLimit", "$Notebooks", "$NumberMarks", 73 | "$OperatingSystem", "$Output", "$OutputSizeLimit", "$OutputStreamMethods", 74 | "$Packages", "$ParentLink", "$ParentProcessID", "$PasswordFile", "$Path", 75 | "$PathnameSeparator", "$PerformanceGoal", "$Permissions", "$PlotTheme", 76 | "$Printout3DPreviewer", "$ProcessID", "$ProcessorCount", "$ProcessorType", 77 | "$ProgressReporting", "$RandomGeneratorState", "$RecursionLimit", 78 | "$ReleaseNumber", "$RequesterAddress", "$RequesterCloudUserID", 79 | "$RequesterCloudUserUUID", "$RequesterWolframID", "$RequesterWolframUUID", 80 | "$RootDirectory", "$ScriptCommandLine", "$ScriptInputString", "$Services", 81 | "$SessionID", "$SharedFunctions", "$SharedVariables", 82 | "$SoundDisplayFunction", "$SynchronousEvaluation", "$System", 83 | "$SystemCharacterEncoding", "$SystemID", "$SystemShell", "$SystemTimeZone", 84 | "$SystemWordLength", "$TemplatePath", "$TemporaryDirectory", "$TimedOut", 85 | "$TimeUnit", "$TimeZone", "$TimeZoneEntity", "$UnitSystem", "$Urgent", 86 | "$UserAgentString", "$UserBaseDirectory", "$UserBasePacletsDirectory", 87 | "$UserDocumentsDirectory", "$Username", "$UserURLBase", "$Version", 88 | "$VersionNumber", "$WolframDocumentsDirectory", "$WolframID", "$WolframUUID"} 89 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/ObsoleteSymbols.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"AbortScheduledTask", "Active", "AlgebraicRules", "AlternateImage", 5 | "AnatomyForm", "AnimationCycleOffset", "AnimationCycleRepetitions", 6 | "AnimationDisplayTime", "AspectRatioFixed", "AstronomicalData", 7 | "AsynchronousTaskObject", "AsynchronousTasks", "AudioDevice", 8 | "AudioLooping", "ButtonEvaluator", "ButtonExpandable", "ButtonFrame", 9 | "ButtonMargins", "ButtonNote", "ButtonStyle", "CDFInformation", 10 | "ChebyshevDistance", "ClassifierInformation", "ClipFill", "ColorOutput", 11 | "ColumnForm", "Compose", "ConstantArrayLayer", "ConstantPlusLayer", 12 | "ConstantTimesLayer", "ConstrainedMax", "ConstrainedMin", "ContourGraphics", 13 | "ContourLines", "ConversionOptions", "CreateScheduledTask", 14 | "CreateTemporary", "Curry", "DatabinRemove", "Date", "Debug", 15 | "DefaultColor", "DefaultFont", "DensityGraphics", "Display", 16 | "DisplayString", "DotPlusLayer", "DragAndDrop", "EdgeLabeling", 17 | "EdgeRenderingFunction", "EvaluateScheduledTask", "ExpectedValue", 18 | "FactorComplete", "FontForm", "FormTheme", "FromDate", "FullOptions", 19 | "GraphicsArray", "GraphicsSpacing", "GraphStyle", "GridBaseline", 20 | "HeldPart", "HiddenSurface", "HomeDirectory", "HTMLSave", "ImageRotated", 21 | "InstanceNormalizationLayer", "LegendreType", "LightSources", 22 | "LinearProgramming", "LinkOpen", "Literal", "LongestMatch", 23 | "LUBackSubstitution", "MeshRange", "MoleculeEquivalentQ", "NetInformation", 24 | "NetSharedArray", "NextScheduledTaskTime", "NotebookCreate", 25 | "OpenTemporary", "PackingMethod", "PersistentValue", "PIDData", 26 | "PixelConstrained", "Plot3Matrix", "PlotDivision", "PlotJoined", 27 | "PolygonIntersections", "PredictorInformation", "Properties", "Property", 28 | "PropertyList", "PropertyValue", "Random", "RasterArray", 29 | "RecognitionThreshold", "Release", "RemoteKernelObject", 30 | "RemoveAsynchronousTask", "RemoveProperty", "RemoveScheduledTask", 31 | "RenderAll", "ReplaceHeldPart", "ResetScheduledTask", "ResumePacket", 32 | "RunScheduledTask", "ScheduledTaskActiveQ", "ScheduledTaskInformation", 33 | "ScheduledTaskObject", "ScheduledTasks", "ScreenRectangle", 34 | "SelectionAnimate", "SequenceAttentionLayer", "SequenceForm", "SetProperty", 35 | "Shading", "ShortestMatch", "SingularValues", "SkinStyle", 36 | "SocialMediaData", "StartAsynchronousTask", "StartScheduledTask", 37 | "StateDimensions", "StopAsynchronousTask", "StopScheduledTask", 38 | "StructuredArray", "StyleForm", "StylePrint", "Subscripted", "SurfaceColor", 39 | "SurfaceGraphics", "SuspendPacket", "SystemModelProgressReporting", 40 | "TeXSave", "TextStyle", "TimeWarpingCorrespondence", "TimeWarpingDistance", 41 | "ToDate", "ToFileName", "ToHeldExpression", "URLFetch", 42 | "URLFetchAsynchronous", "URLSave", "URLSaveAsynchronous", "VectorScale", 43 | "VertexCoordinateRules", "VertexLabeling", "VertexRenderingFunction", 44 | "WaitAsynchronousTask", "WindowMovable", "$AsynchronousTask", 45 | "$ConfiguredKernels", "$DefaultFont", "$EntityStores", "$FormatType", 46 | "$HTTPCookies", "$InstallationDate", "$MachineDomain", 47 | "$ProductInformation", "$ProgramName", "$RandomState", "$ScheduledTask", 48 | "$SummaryBoxDataSizeLimit", "$TemporaryPrefix", "$TextStyle", 49 | "$TopDirectory", "$UserAddOnsDirectory"} 50 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/SessionSymbols.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"Echo", "Exit", "In", "InString", "Out", "Print", "Quit", "$HistoryLength", 5 | "$Line", "$Post", "$Pre", "$PrePrint", "$PreRead", "$SyntaxHandler"} 6 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/SpecialCharacters.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"\\[Degree]", "\\[Pi]", "\\[Infinity]", "\\[ExponentialE]", 5 | "\\[ImaginaryI]", "\\[ImaginaryJ]"} 6 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/SpecialLongNames.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"Degree", "Pi", "Infinity", "ExponentialE", "ImaginaryI", "ImaginaryJ"} 5 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/SystemCharacters.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"\\[SystemsModelDelay]", "\\[FormalA]", "\\[FormalB]", "\\[FormalC]", 5 | "\\[FormalD]", "\\[FormalE]", "\\[FormalF]", "\\[FormalG]", "\\[FormalH]", 6 | "\\[FormalI]", "\\[FormalJ]", "\\[FormalK]", "\\[FormalL]", "\\[FormalM]", 7 | "\\[FormalN]", "\\[FormalO]", "\\[FormalP]", "\\[FormalQ]", "\\[FormalR]", 8 | "\\[FormalS]", "\\[FormalT]", "\\[FormalU]", "\\[FormalV]", "\\[FormalW]", 9 | "\\[FormalX]", "\\[FormalY]", "\\[FormalZ]", "\\[FormalCapitalA]", 10 | "\\[FormalCapitalB]", "\\[FormalCapitalC]", "\\[FormalCapitalD]", 11 | "\\[FormalCapitalE]", "\\[FormalCapitalF]", "\\[FormalCapitalG]", 12 | "\\[FormalCapitalH]", "\\[FormalCapitalI]", "\\[FormalCapitalJ]", 13 | "\\[FormalCapitalK]", "\\[FormalCapitalL]", "\\[FormalCapitalM]", 14 | "\\[FormalCapitalN]", "\\[FormalCapitalO]", "\\[FormalCapitalP]", 15 | "\\[FormalCapitalQ]", "\\[FormalCapitalR]", "\\[FormalCapitalS]", 16 | "\\[FormalCapitalT]", "\\[FormalCapitalU]", "\\[FormalCapitalV]", 17 | "\\[FormalCapitalW]", "\\[FormalCapitalX]", "\\[FormalCapitalY]", 18 | "\\[FormalCapitalZ]", "\\[FormalCapitalAlpha]", "\\[FormalCapitalBeta]", 19 | "\\[FormalCapitalGamma]", "\\[FormalCapitalDelta]", 20 | "\\[FormalCapitalEpsilon]", "\\[FormalCapitalZeta]", "\\[FormalCapitalEta]", 21 | "\\[FormalCapitalTheta]", "\\[FormalCapitalIota]", "\\[FormalCapitalKappa]", 22 | "\\[FormalCapitalLambda]", "\\[FormalCapitalMu]", "\\[FormalCapitalNu]", 23 | "\\[FormalCapitalXi]", "\\[FormalCapitalOmicron]", "\\[FormalCapitalPi]", 24 | "\\[FormalCapitalRho]", "\\[FormalCapitalSigma]", "\\[FormalCapitalTau]", 25 | "\\[FormalCapitalUpsilon]", "\\[FormalCapitalPhi]", "\\[FormalCapitalChi]", 26 | "\\[FormalCapitalPsi]", "\\[FormalCapitalOmega]", "\\[FormalAlpha]", 27 | "\\[FormalBeta]", "\\[FormalGamma]", "\\[FormalDelta]", 28 | "\\[FormalCurlyEpsilon]", "\\[FormalZeta]", "\\[FormalEta]", 29 | "\\[FormalTheta]", "\\[FormalIota]", "\\[FormalKappa]", "\\[FormalLambda]", 30 | "\\[FormalMu]", "\\[FormalNu]", "\\[FormalXi]", "\\[FormalOmicron]", 31 | "\\[FormalPi]", "\\[FormalRho]", "\\[FormalFinalSigma]", "\\[FormalSigma]", 32 | "\\[FormalTau]", "\\[FormalUpsilon]", "\\[FormalCurlyPhi]", "\\[FormalChi]", 33 | "\\[FormalPsi]", "\\[FormalOmega]", "\\[FormalCurlyTheta]", 34 | "\\[FormalCurlyCapitalUpsilon]", "\\[FormalPhi]", "\\[FormalCurlyPi]", 35 | "\\[FormalCapitalStigma]", "\\[FormalStigma]", "\\[FormalCapitalDigamma]", 36 | "\\[FormalDigamma]", "\\[FormalCapitalKoppa]", "\\[FormalKoppa]", 37 | "\\[FormalCapitalSampi]", "\\[FormalSampi]", "\\[FormalCurlyKappa]", 38 | "\\[FormalCurlyRho]", "\\[FormalEpsilon]", "\\[FormalScriptA]", 39 | "\\[FormalScriptB]", "\\[FormalScriptC]", "\\[FormalScriptD]", 40 | "\\[FormalScriptE]", "\\[FormalScriptF]", "\\[FormalScriptG]", 41 | "\\[FormalScriptH]", "\\[FormalScriptI]", "\\[FormalScriptJ]", 42 | "\\[FormalScriptK]", "\\[FormalScriptL]", "\\[FormalScriptM]", 43 | "\\[FormalScriptN]", "\\[FormalScriptO]", "\\[FormalScriptP]", 44 | "\\[FormalScriptQ]", "\\[FormalScriptR]", "\\[FormalScriptS]", 45 | "\\[FormalScriptT]", "\\[FormalScriptU]", "\\[FormalScriptV]", 46 | "\\[FormalScriptW]", "\\[FormalScriptX]", "\\[FormalScriptY]", 47 | "\\[FormalScriptZ]", "\\[FormalScriptCapitalA]", "\\[FormalScriptCapitalB]", 48 | "\\[FormalScriptCapitalC]", "\\[FormalScriptCapitalD]", 49 | "\\[FormalScriptCapitalE]", "\\[FormalScriptCapitalF]", 50 | "\\[FormalScriptCapitalG]", "\\[FormalScriptCapitalH]", 51 | "\\[FormalScriptCapitalI]", "\\[FormalScriptCapitalJ]", 52 | "\\[FormalScriptCapitalK]", "\\[FormalScriptCapitalL]", 53 | "\\[FormalScriptCapitalM]", "\\[FormalScriptCapitalN]", 54 | "\\[FormalScriptCapitalO]", "\\[FormalScriptCapitalP]", 55 | "\\[FormalScriptCapitalQ]", "\\[FormalScriptCapitalR]", 56 | "\\[FormalScriptCapitalS]", "\\[FormalScriptCapitalT]", 57 | "\\[FormalScriptCapitalU]", "\\[FormalScriptCapitalV]", 58 | "\\[FormalScriptCapitalW]", "\\[FormalScriptCapitalX]", 59 | "\\[FormalScriptCapitalY]", "\\[FormalScriptCapitalZ]"} 60 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/SystemLongNames.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"SystemsModelDelay", "FormalA", "FormalB", "FormalC", "FormalD", "FormalE", 5 | "FormalF", "FormalG", "FormalH", "FormalI", "FormalJ", "FormalK", "FormalL", 6 | "FormalM", "FormalN", "FormalO", "FormalP", "FormalQ", "FormalR", "FormalS", 7 | "FormalT", "FormalU", "FormalV", "FormalW", "FormalX", "FormalY", "FormalZ", 8 | "FormalCapitalA", "FormalCapitalB", "FormalCapitalC", "FormalCapitalD", 9 | "FormalCapitalE", "FormalCapitalF", "FormalCapitalG", "FormalCapitalH", 10 | "FormalCapitalI", "FormalCapitalJ", "FormalCapitalK", "FormalCapitalL", 11 | "FormalCapitalM", "FormalCapitalN", "FormalCapitalO", "FormalCapitalP", 12 | "FormalCapitalQ", "FormalCapitalR", "FormalCapitalS", "FormalCapitalT", 13 | "FormalCapitalU", "FormalCapitalV", "FormalCapitalW", "FormalCapitalX", 14 | "FormalCapitalY", "FormalCapitalZ", "FormalCapitalAlpha", 15 | "FormalCapitalBeta", "FormalCapitalGamma", "FormalCapitalDelta", 16 | "FormalCapitalEpsilon", "FormalCapitalZeta", "FormalCapitalEta", 17 | "FormalCapitalTheta", "FormalCapitalIota", "FormalCapitalKappa", 18 | "FormalCapitalLambda", "FormalCapitalMu", "FormalCapitalNu", 19 | "FormalCapitalXi", "FormalCapitalOmicron", "FormalCapitalPi", 20 | "FormalCapitalRho", "FormalCapitalSigma", "FormalCapitalTau", 21 | "FormalCapitalUpsilon", "FormalCapitalPhi", "FormalCapitalChi", 22 | "FormalCapitalPsi", "FormalCapitalOmega", "FormalAlpha", "FormalBeta", 23 | "FormalGamma", "FormalDelta", "FormalCurlyEpsilon", "FormalZeta", 24 | "FormalEta", "FormalTheta", "FormalIota", "FormalKappa", "FormalLambda", 25 | "FormalMu", "FormalNu", "FormalXi", "FormalOmicron", "FormalPi", 26 | "FormalRho", "FormalFinalSigma", "FormalSigma", "FormalTau", 27 | "FormalUpsilon", "FormalCurlyPhi", "FormalChi", "FormalPsi", "FormalOmega", 28 | "FormalCurlyTheta", "FormalCurlyCapitalUpsilon", "FormalPhi", 29 | "FormalCurlyPi", "FormalCapitalStigma", "FormalStigma", 30 | "FormalCapitalDigamma", "FormalDigamma", "FormalCapitalKoppa", 31 | "FormalKoppa", "FormalCapitalSampi", "FormalSampi", "FormalCurlyKappa", 32 | "FormalCurlyRho", "FormalEpsilon", "FormalScriptA", "FormalScriptB", 33 | "FormalScriptC", "FormalScriptD", "FormalScriptE", "FormalScriptF", 34 | "FormalScriptG", "FormalScriptH", "FormalScriptI", "FormalScriptJ", 35 | "FormalScriptK", "FormalScriptL", "FormalScriptM", "FormalScriptN", 36 | "FormalScriptO", "FormalScriptP", "FormalScriptQ", "FormalScriptR", 37 | "FormalScriptS", "FormalScriptT", "FormalScriptU", "FormalScriptV", 38 | "FormalScriptW", "FormalScriptX", "FormalScriptY", "FormalScriptZ", 39 | "FormalScriptCapitalA", "FormalScriptCapitalB", "FormalScriptCapitalC", 40 | "FormalScriptCapitalD", "FormalScriptCapitalE", "FormalScriptCapitalF", 41 | "FormalScriptCapitalG", "FormalScriptCapitalH", "FormalScriptCapitalI", 42 | "FormalScriptCapitalJ", "FormalScriptCapitalK", "FormalScriptCapitalL", 43 | "FormalScriptCapitalM", "FormalScriptCapitalN", "FormalScriptCapitalO", 44 | "FormalScriptCapitalP", "FormalScriptCapitalQ", "FormalScriptCapitalR", 45 | "FormalScriptCapitalS", "FormalScriptCapitalT", "FormalScriptCapitalU", 46 | "FormalScriptCapitalV", "FormalScriptCapitalW", "FormalScriptCapitalX", 47 | "FormalScriptCapitalY", "FormalScriptCapitalZ"} 48 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/UndocumentedCharacters.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"\\[Akuz]", "\\[Andy]", "\\[ContinuedFractionK]", "\\[Curl]", 5 | "\\[Divergence]", "\\[DivisionSlash]", "\\[ExpectationE]", 6 | "\\[FreeformPrompt]", "\\[Gradient]", "\\[Laplacian]", "\\[Minus]", 7 | "\\[Moon]", "\\[NumberComma]", "\\[PageBreakAbove]", "\\[PageBreakBelow]", 8 | "\\[ProbabilityPr]", "\\[Spooky]", "\\[StepperDown]", "\\[StepperLeft]", 9 | "\\[StepperRight]", "\\[StepperUp]", "\\[Sun]", "\\[UnknownGlyph]", 10 | "\\[Villa]", "\\[WolframAlphaPrompt]"} 11 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/UndocumentedLongNames.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"Akuz", "Andy", "ContinuedFractionK", "Curl", "Divergence", "DivisionSlash", 5 | "ExpectationE", "FreeformPrompt", "Gradient", "Laplacian", "Minus", "Moon", 6 | "NumberComma", "PageBreakAbove", "PageBreakBelow", "ProbabilityPr", 7 | "Spooky", "StepperDown", "StepperLeft", "StepperRight", "StepperUp", "Sun", 8 | "UnknownGlyph", "Villa", "WolframAlphaPrompt"} 9 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/UnsupportedCharacters.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"\\[COMPATIBILITYKanjiSpace]", "\\[COMPATIBILITYNoBreak]", "\\[InlinePart]"} 5 | -------------------------------------------------------------------------------- /WolframLanguageSyntax/Data/UnsupportedLongNames.wl: -------------------------------------------------------------------------------- 1 | (* AUTO GENERATED FILE *) 2 | (* DO NOT MODIFY *) 3 | (* GENERATED WITH: 13.2.0 for Mac OS X x86 (64-bit) (November 18, 2022) *) 4 | {"COMPATIBILITYKanjiSpace", "COMPATIBILITYNoBreak", "InlinePart"} 5 | -------------------------------------------------------------------------------- /cmake/InspectFile.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(NOT CODEPARSER_EXE) 3 | return() 4 | endif() 5 | 6 | if(NOT EXISTS ${CODEPARSER_EXE}) 7 | return() 8 | endif() 9 | 10 | execute_process( 11 | COMMAND 12 | ${CODEPARSER_EXE} -check -file ${SRC} 13 | RESULT_VARIABLE 14 | CODEPARSER_RESULT 15 | ) 16 | 17 | if(${CODEPARSER_RESULT} EQUAL "0") 18 | return() 19 | endif() 20 | 21 | if(NOT ${CODEPARSER_RESULT} EQUAL "1") 22 | message(WARNING "Internal error. CODEPARSER_RESULT: ${CODEPARSER_RESULT}") 23 | return() 24 | endif() 25 | 26 | # 27 | # We know there was some problem, so now use CodeInspector to report the problem 28 | # 29 | 30 | if(NOT WOLFRAMKERNEL) 31 | return() 32 | endif() 33 | 34 | if(NOT EXISTS ${WOLFRAMKERNEL}) 35 | return() 36 | endif() 37 | 38 | set(CODE "\ 39 | If[FailureQ[FindFile[\"CodeInspector`\"]], Exit[0]]\;\ 40 | Needs[\"CodeInspector`\"]\;\ 41 | Print[\"Code inspection...\" //OutputForm]\;\ 42 | Print[CodeInspector`CodeInspectSummarize[File[\"${SRC}\"]] //OutputForm]\;\ 43 | Exit[1]\ 44 | ") 45 | 46 | execute_process( 47 | COMMAND 48 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -run ${CODE} 49 | TIMEOUT 50 | ${KERNEL_TIMEOUT} 51 | ) 52 | 53 | message(FATAL_ERROR "File had fatal errors: ${SRC}") 54 | -------------------------------------------------------------------------------- /cmake/InstallPaclet.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(NOT EXISTS ${WOLFRAMKERNEL}) 3 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 4 | endif() 5 | 6 | set(CODE "\ 7 | Print[OutputForm[\"Calling PacletInstall...\"]]\; 8 | Check[ 9 | res = PacletInstall[\"${PACLET_OUTPUT}\", ForceVersionInstall -> True]\; 10 | , 11 | Print[OutputForm[Row[{\"$VersionNumber: \", NumberForm[$VersionNumber, {2, 1}]}]]]\; 12 | Print[OutputForm[Row[{\"Paclet WolframVersion: \", \"${PACLET_WOLFRAMVERSION}\"}]]]\; 13 | Print[OutputForm[Row[{\"To prevent this PacletInstall::compat message, update PacletInfo.wl.in with WolframVersion -> \\\"\", NumberForm[$VersionNumber, {2, 1}] ,\"\\\" and build and install again.\"}]]]; 14 | res 15 | , 16 | {PacletInstall::compat} 17 | ]\; 18 | Print[res //OutputForm]\; 19 | Print[OutputForm[\"Done PacletInstall\"]]\; 20 | If[!PacletObjectQ[res], 21 | Exit[1] 22 | ]\; 23 | Exit[0] 24 | ") 25 | 26 | execute_process( 27 | COMMAND 28 | ${WOLFRAMKERNEL} -noinit -noprompt -run ${CODE} 29 | TIMEOUT 30 | ${KERNEL_TIMEOUT} 31 | RESULT_VARIABLE 32 | INSTALL_RESULT 33 | ) 34 | 35 | if(NOT ${INSTALL_RESULT} EQUAL "0") 36 | message(FATAL_ERROR "Bad exit code from install: ${INSTALL_RESULT}") 37 | endif() 38 | -------------------------------------------------------------------------------- /cmake/MacOSXVersionMin.cmake: -------------------------------------------------------------------------------- 1 | 2 | macro(CheckMacOSXVersionMin) 3 | 4 | if(NOT EXISTS ${WOLFRAMKERNEL}) 5 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 6 | endif() 7 | 8 | execute_process( 9 | COMMAND 10 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -run Pause[${KERNEL_PAUSE}]\;Needs["CCompilerDriver`"]\;Print[OutputForm[StringReplace[CCompilerDriver`CCompilerDriverBase`MacOSXVersionMinFlag[],\ "-mmacosx-version-min="\ ->\ ""]]]\;Exit[] 11 | OUTPUT_VARIABLE 12 | MACOSX_VERSION_MIN 13 | OUTPUT_STRIP_TRAILING_WHITESPACE 14 | WORKING_DIRECTORY 15 | ${PROJECT_SOURCE_DIR} 16 | TIMEOUT 17 | ${KERNEL_TIMEOUT} 18 | RESULT_VARIABLE 19 | MACOSX_VERSION_MIN_RESULT 20 | ) 21 | 22 | if(NOT ${MACOSX_VERSION_MIN_RESULT} EQUAL "0") 23 | message(FATAL_ERROR "Bad exit code from MacOSXVersionMin script: ${MACOSX_VERSION_MIN_RESULT}") 24 | endif() 25 | 26 | endmacro(CheckMacOSXVersionMin) 27 | -------------------------------------------------------------------------------- /cmake/PacletInfo.cmake: -------------------------------------------------------------------------------- 1 | 2 | macro(CheckPacletInfo) 3 | 4 | if(NOT EXISTS ${WOLFRAMKERNEL}) 5 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 6 | endif() 7 | 8 | if(LOCAL_BUILD) 9 | message(STATUS "Paclet Version ignored in local build") 10 | set(LOCAL_BUILD_VERSION 999.9) 11 | else() 12 | # 13 | # if not local build, then get Version from PacletInfo.wl 14 | # 15 | execute_process( 16 | COMMAND 17 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[Row[{Version,\ ";",\ WolframVersion}\ /.\ List\ @@\ Get["${PACLETINFO_IN_SOURCE}"]]]]\;Exit[] 18 | OUTPUT_VARIABLE 19 | PACLET_VERSIONS_LIST 20 | OUTPUT_STRIP_TRAILING_WHITESPACE 21 | WORKING_DIRECTORY 22 | ${PROJECT_SOURCE_DIR} 23 | TIMEOUT 24 | ${KERNEL_TIMEOUT} 25 | RESULT_VARIABLE 26 | PACLETINFO_RESULT 27 | ) 28 | 29 | if(NOT ${PACLETINFO_RESULT} EQUAL "0") 30 | message(FATAL_ERROR "Bad exit code from PacletInfo script: ${PACLETINFO_RESULT}") 31 | endif() 32 | 33 | list(GET PACLET_VERSIONS_LIST 0 PACLET_VERSION) 34 | list(GET PACLET_VERSIONS_LIST 1 PACLET_WOLFRAMVERSION) 35 | message(STATUS "PACLET_VERSION: ${PACLET_VERSION}") 36 | message(STATUS "PACLET_WOLFRAMVERSION: ${PACLET_WOLFRAMVERSION}") 37 | 38 | endif(LOCAL_BUILD) 39 | 40 | endmacro(CheckPacletInfo) 41 | -------------------------------------------------------------------------------- /cmake/ReplacePacletInfo.cmake: -------------------------------------------------------------------------------- 1 | 2 | file(READ ${PACLETINFO_IN_SOURCE} filedata) 3 | 4 | string(TIMESTAMP DATESTRING "%a %d %b %Y %H:%M:%S") 5 | 6 | string(REGEX REPLACE "BuildDate -> \"[a-zA-Z0-9 :]*\"" "BuildDate -> \"${DATESTRING}\"" filedata ${filedata}) 7 | 8 | string(REGEX REPLACE "BuildNumber -> [0-9]+" "BuildNumber -> ${BUILDNUMBER}" filedata ${filedata}) 9 | 10 | string(REGEX REPLACE "BuildWolframVersionNumber -> [0-9]+" "BuildWolframVersionNumber -> ${VERSION_NUMBER}" filedata ${filedata}) 11 | 12 | string(REGEX REPLACE "BuildWolframLibraryVersion -> [0-9]+" "BuildWolframLibraryVersion -> ${WOLFRAMLIBRARY_VERSION}" filedata ${filedata}) 13 | 14 | string(REGEX REPLACE "Transport -> \"[a-zA-Z]*\"" "Transport -> \"${TRANSPORT}\"" filedata ${filedata}) 15 | 16 | if(LOCAL_BUILD) 17 | 18 | string(REGEX REPLACE "Version -> \"[0-9\\.]+\"," "Version -> \"${LOCAL_BUILD_VERSION}\"(* local build *)," filedata ${filedata}) 19 | 20 | endif() 21 | 22 | file(WRITE ${REPLACED_PACLETINFO} "${filedata}") 23 | -------------------------------------------------------------------------------- /cmake/WolframKernel.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(NOT DEFINED MATHEMATICA_INSTALL_DIR) 3 | if(CMAKE_HOST_WIN32) 4 | set(MATHEMATICA_INSTALL_DIR "C:/Program Files/Wolfram Research/Mathematica/13.1") 5 | elseif(CMAKE_HOST_APPLE) 6 | set(MATHEMATICA_INSTALL_DIR /Applications/Mathematica.app/Contents) 7 | else() 8 | set(MATHEMATICA_INSTALL_DIR /usr/local/Wolfram/Mathematica/13.1) 9 | endif() 10 | endif() 11 | 12 | if(CMAKE_HOST_WIN32) 13 | set(WOLFRAMKERNEL_DEFAULT ${MATHEMATICA_INSTALL_DIR}/wolfram.exe) 14 | set(WOLFRAMLIBRARY_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/IncludeFiles/C) 15 | # 16 | # in versions before 11.2, there were 2 separate paths: 17 | # SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/include 18 | # SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/lib 19 | # 20 | # starting in 11.2, the single path for MathLink includes and MathLink libs is: 21 | # SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions 22 | # 23 | if(EXISTS ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/include) 24 | set(MATHLINK_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/include) 25 | else() 26 | set(MATHLINK_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions) 27 | endif() 28 | if(EXISTS ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/lib) 29 | set(MATHLINK_LIB_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions/mldev64/lib) 30 | else() 31 | set(MATHLINK_LIB_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Windows-x86-64/CompilerAdditions) 32 | endif() 33 | elseif(CMAKE_HOST_APPLE) 34 | set(WOLFRAMKERNEL_DEFAULT ${MATHEMATICA_INSTALL_DIR}/MacOS/WolframKernel) 35 | set(WOLFRAMLIBRARY_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/IncludeFiles/C) 36 | set(MATHLINK_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/MacOSX-x86-64/CompilerAdditions) 37 | set(MATHLINK_LIB_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/MacOSX-x86-64/CompilerAdditions) 38 | else() 39 | set(WOLFRAMKERNEL_DEFAULT ${MATHEMATICA_INSTALL_DIR}/Executables/WolframKernel) 40 | set(WOLFRAMLIBRARY_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/IncludeFiles/C) 41 | set(MATHLINK_INCLUDE_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Linux-x86-64/CompilerAdditions) 42 | set(MATHLINK_LIB_DIR_DEFAULT ${MATHEMATICA_INSTALL_DIR}/SystemFiles/Links/MathLink/DeveloperKit/Linux-x86-64/CompilerAdditions) 43 | endif() 44 | 45 | macro(CheckWolframKernel) 46 | 47 | if(NOT EXISTS ${WOLFRAMKERNEL}) 48 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 49 | endif() 50 | 51 | # 52 | # get $Version 53 | # 54 | execute_process( 55 | COMMAND 56 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[$Version]]\;Exit[] 57 | OUTPUT_VARIABLE 58 | VERSION 59 | OUTPUT_STRIP_TRAILING_WHITESPACE 60 | WORKING_DIRECTORY 61 | ${PROJECT_SOURCE_DIR} 62 | TIMEOUT 63 | ${KERNEL_TIMEOUT} 64 | RESULT_VARIABLE 65 | VERSION_RESULT 66 | ) 67 | 68 | message(STATUS "VERSION: ${VERSION}") 69 | 70 | if(NOT ${VERSION_RESULT} EQUAL "0") 71 | message(WARNING "Bad exit code from Version script: ${VERSION_RESULT}; Continuing") 72 | endif() 73 | 74 | # 75 | # get $VersionNumber 76 | # 77 | execute_process( 78 | COMMAND 79 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[Floor[100\ $VersionNumber\ +\ $ReleaseNumber]]]\;Exit[] 80 | OUTPUT_VARIABLE 81 | VERSION_NUMBER 82 | OUTPUT_STRIP_TRAILING_WHITESPACE 83 | WORKING_DIRECTORY 84 | ${PROJECT_SOURCE_DIR} 85 | TIMEOUT 86 | ${KERNEL_TIMEOUT} 87 | RESULT_VARIABLE 88 | VERSION_NUMBER_RESULT 89 | ) 90 | 91 | message(STATUS "VERSION_NUMBER: ${VERSION_NUMBER}") 92 | 93 | if(NOT ${VERSION_NUMBER} GREATER_EQUAL 1100) 94 | message(FATAL_ERROR "Wolfram Kernel must be at least version 11.0: ${VERSION_NUMBER}") 95 | endif() 96 | 97 | if(NOT ${VERSION_NUMBER_RESULT} EQUAL "0") 98 | message(WARNING "Bad exit code from VersionNumber script: ${VERSION_NUMBER_RESULT}; Continuing") 99 | endif() 100 | 101 | # 102 | # get $SystemID 103 | # 104 | execute_process( 105 | COMMAND 106 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[$SystemID]]\;Exit[] 107 | OUTPUT_VARIABLE 108 | SYSTEMID 109 | OUTPUT_STRIP_TRAILING_WHITESPACE 110 | WORKING_DIRECTORY 111 | ${PROJECT_SOURCE_DIR} 112 | TIMEOUT 113 | ${KERNEL_TIMEOUT} 114 | RESULT_VARIABLE 115 | SYSTEMID_RESULT 116 | ) 117 | 118 | message(STATUS "SYSTEMID: ${SYSTEMID}") 119 | 120 | if(NOT ${SYSTEMID_RESULT} EQUAL "0") 121 | message(WARNING "Bad exit code from SystemID script: ${SYSTEMID_RESULT}; Continuing") 122 | endif() 123 | 124 | # 125 | # get $SystemWordLength 126 | # 127 | execute_process( 128 | COMMAND 129 | ${WOLFRAMKERNEL} -noinit -noprompt -nopaclet -nostartuppaclets -runfirst Pause[${KERNEL_PAUSE}]\;Print[OutputForm[$SystemWordLength]]\;Exit[] 130 | OUTPUT_VARIABLE 131 | SYSTEMWORDLENGTH 132 | OUTPUT_STRIP_TRAILING_WHITESPACE 133 | WORKING_DIRECTORY 134 | ${PROJECT_SOURCE_DIR} 135 | TIMEOUT 136 | ${KERNEL_TIMEOUT} 137 | RESULT_VARIABLE 138 | SYSTEMWORDLENGTH_RESULT 139 | ) 140 | 141 | message(STATUS "SYSTEMWORDLENGTH: ${SYSTEMWORDLENGTH}") 142 | 143 | if(NOT ${SYSTEMWORDLENGTH_RESULT} EQUAL "0") 144 | message(WARNING "Bad exit code from SystemWordLength script: ${SYSTEMWORDLENGTH_RESULT}; Continuing") 145 | endif() 146 | 147 | # 148 | # Make sure that CMake and Mathematica agree about 32-bit or 64-bit 149 | # 150 | if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") 151 | # CMAKE_SIZEOF_VOID_P is not set; CXX is probably not enabled 152 | elseif(${CMAKE_SIZEOF_VOID_P} EQUAL 4) 153 | if(NOT ${SYSTEMWORDLENGTH} EQUAL 32) 154 | message(FATAL_ERROR 155 | "CMake is reporting 32-bit; Mathematica is reporting: ${SYSTEMWORDLENGTH}\n" 156 | "HINT: On Windows, you probably need to specify -A x64" 157 | ) 158 | endif() 159 | elseif(${CMAKE_SIZEOF_VOID_P} EQUAL 8) 160 | if(NOT ${SYSTEMWORDLENGTH} EQUAL 64) 161 | message(FATAL_ERROR "CMake is reporting 64-bit; Mathematica is reporting: ${SYSTEMWORDLENGTH}") 162 | endif() 163 | else() 164 | message(FATAL_ERROR "CMake is reporting neither 32-bit nor 64-bit. CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}") 165 | endif() 166 | 167 | endmacro(CheckWolframKernel) 168 | -------------------------------------------------------------------------------- /cmake/WolframLibrary.cmake: -------------------------------------------------------------------------------- 1 | 2 | macro(ParseWolframLibraryHeader) 3 | 4 | if(NOT EXISTS ${WOLFRAMLIBRARY_INCLUDE_DIR}) 5 | message(FATAL_ERROR "WOLFRAMLIBRARY_INCLUDE_DIR does not exist. WOLFRAMLIBRARY_INCLUDE_DIR: ${WOLFRAMLIBRARY_INCLUDE_DIR}") 6 | endif() 7 | 8 | set(WOLFRAMLIBRARY_HEADER ${WOLFRAMLIBRARY_INCLUDE_DIR}/WolframLibrary.h) 9 | 10 | if(NOT EXISTS ${WOLFRAMLIBRARY_HEADER}) 11 | message(FATAL_ERROR "WOLFRAMLIBRARY_HEADER does not exist. WOLFRAMLIBRARY_HEADER: ${WOLFRAMLIBRARY_HEADER}") 12 | endif() 13 | 14 | file(READ ${WOLFRAMLIBRARY_HEADER} filedata) 15 | 16 | string(REGEX MATCH "#define WolframLibraryVersion ([0-9]+)" _ ${filedata}) 17 | 18 | set(WOLFRAMLIBRARY_VERSION ${CMAKE_MATCH_1}) 19 | 20 | if(NOT DEFINED WOLFRAMLIBRARY_VERSION) 21 | message(FATAL_ERROR "WOLFRAMLIBRARY_VERSION was not set.") 22 | endif() 23 | 24 | endmacro(ParseWolframLibraryHeader) 25 | -------------------------------------------------------------------------------- /cmake/WolframScript.cmake: -------------------------------------------------------------------------------- 1 | 2 | if(NOT EXISTS ${WOLFRAMKERNEL}) 3 | message(FATAL_ERROR "WOLFRAMKERNEL does not exist. WOLFRAMKERNEL: ${WOLFRAMKERNEL}") 4 | endif() 5 | 6 | if(NOT DEFINED RETRY_ON_FAILURE) 7 | set(RETRY_ON_FAILURE OFF) 8 | endif() 9 | 10 | if(NOT EXISTS ${SCRIPT}) 11 | message(FATAL_ERROR "SCRIPT does not exist. SCRIPT: ${SCRIPT}") 12 | endif() 13 | 14 | file(READ ${SCRIPT} script) 15 | 16 | if(script STREQUAL "") 17 | message(FATAL_ERROR "SCRIPT is empty. SCRIPT: ${SCRIPT}") 18 | endif() 19 | 20 | if(RETRY_ON_FAILURE) 21 | 22 | # 23 | # try twice 24 | # 25 | 26 | execute_process( 27 | COMMAND 28 | ${WOLFRAMKERNEL} -script ${SCRIPT} -srcDir ${SRCDIR} -buildDir ${BUILDDIR} -pacletLayoutDir ${PACLET_LAYOUT_DIR} -paclet ${PACLET} 29 | TIMEOUT 30 | ${KERNEL_TIMEOUT} 31 | RESULT_VARIABLE 32 | SCRIPT_RESULT 33 | ) 34 | 35 | if(NOT ${SCRIPT_RESULT} EQUAL "0") 36 | message(WARNING "First try: Bad exit code from script: ${SCRIPT_RESULT}; retrying...") 37 | 38 | execute_process( 39 | COMMAND 40 | ${WOLFRAMKERNEL} -retry -script ${SCRIPT} -srcDir ${SRCDIR} -buildDir ${BUILDDIR} -pacletLayoutDir ${PACLET_LAYOUT_DIR} -paclet ${PACLET} 41 | TIMEOUT 42 | ${KERNEL_TIMEOUT} 43 | RESULT_VARIABLE 44 | SCRIPT_RESULT 45 | ) 46 | 47 | if(NOT ${SCRIPT_RESULT} EQUAL "0") 48 | message(FATAL_ERROR "Second try: Bad exit code from script: ${SCRIPT_RESULT}; stopping") 49 | else() 50 | message(STATUS "Second try: Success!") 51 | endif() 52 | 53 | endif() 54 | 55 | else(RETRY_ON_FAILURE) 56 | 57 | # 58 | # only try once 59 | # 60 | 61 | execute_process( 62 | COMMAND 63 | ${WOLFRAMKERNEL} -script ${SCRIPT} -srcDir ${SRCDIR} -buildDir ${BUILDDIR} -pacletLayoutDir ${PACLET_LAYOUT_DIR} -paclet ${PACLET} 64 | TIMEOUT 65 | ${KERNEL_TIMEOUT} 66 | RESULT_VARIABLE 67 | SCRIPT_RESULT 68 | ) 69 | 70 | if(NOT ${SCRIPT_RESULT} EQUAL "0") 71 | message(FATAL_ERROR "Bad exit code from script: ${SCRIPT_RESULT}") 72 | endif() 73 | 74 | endif() 75 | -------------------------------------------------------------------------------- /cpp/include/LSPServer.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | // 5 | // Despite being mentioned here: 6 | // language/LibraryLink/tutorial/LibraryStructure.html 7 | // 8 | // It is not actually possible to include "wstp.h" for use with WolframLibrary.h 9 | // 10 | // Using wstp.h results in errors like: 11 | // error: unknown type name 'MLINK' 12 | // 13 | // Related bugs: 357133 14 | // 15 | // This bug was fixed in v12.0 16 | // 17 | // When we no longer support any version < 12.0, then we can switch to using WSTP 18 | // 19 | // Also be a good citizen and cleanup the leftover defines from mathlink and WolframLibrary 20 | // 21 | #if USE_MATHLINK 22 | #include "mathlink.h" 23 | #undef P 24 | #endif // USE_MATHLINK 25 | 26 | #include "WolframLibrary.h" 27 | #undef True 28 | #undef False 29 | 30 | // 31 | // CMake defines codeparser_lib_EXPORTS 32 | // 33 | #ifdef _WIN32 34 | # ifdef lspserver_lib_EXPORTS 35 | # define LSPSERVERLIB_EXPORTED __declspec( dllexport ) 36 | # else 37 | # define LSPSERVERLIB_EXPORTED __declspec( dllimport ) 38 | # endif 39 | #else 40 | # define LSPSERVERLIB_EXPORTED 41 | #endif 42 | 43 | 44 | constexpr int FILENO_ERROR = 1; 45 | constexpr int FILENO_NOT_STREAM = 2; 46 | constexpr int SETMODE_ERROR = 3; 47 | 48 | constexpr int FREAD_FAILED = 1; 49 | constexpr int UNEXPECTED_LINEFEED = 2; 50 | constexpr int EXPECTED_LINEFEED = 3; 51 | constexpr int UNRECOGNIZED_HEADER = 4; 52 | constexpr int FWRITE_FAILED = 5; 53 | constexpr int FFLUSH_FAILED = 6; 54 | 55 | 56 | constexpr int DEBUG_VERBOSE = 3; 57 | 58 | 59 | EXTERN_C DLLEXPORT mint WolframLibrary_getVersion(); 60 | 61 | EXTERN_C DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData); 62 | 63 | EXTERN_C DLLEXPORT void WolframLibrary_uninitialize(WolframLibraryData libData); 64 | 65 | EXTERN_C DLLEXPORT int SetDebug_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 66 | 67 | EXTERN_C DLLEXPORT int GetStartupError_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 68 | 69 | EXTERN_C DLLEXPORT int ReadLineFromStdIn_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 70 | 71 | EXTERN_C DLLEXPORT int WriteLineToStdOut_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 72 | 73 | EXTERN_C DLLEXPORT int WriteBytesToStdOut_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 74 | 75 | 76 | EXTERN_C DLLEXPORT int StartBackgroundReaderThread_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 77 | 78 | EXTERN_C DLLEXPORT int LockQueue_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 79 | 80 | EXTERN_C DLLEXPORT int UnlockQueue_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 81 | 82 | EXTERN_C DLLEXPORT int GetQueueSize_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 83 | 84 | EXTERN_C DLLEXPORT int GetFrontMessageSize_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 85 | 86 | EXTERN_C DLLEXPORT int PopQueue_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 87 | 88 | EXTERN_C DLLEXPORT int GetStdInFEOF_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 89 | 90 | EXTERN_C DLLEXPORT int GetStdInFError_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 91 | 92 | EXTERN_C DLLEXPORT int GetStdOutFEOF_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 93 | 94 | EXTERN_C DLLEXPORT int GetStdOutFError_LibraryLink(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res); 95 | -------------------------------------------------------------------------------- /docs/compatibility.md: -------------------------------------------------------------------------------- 1 | 2 | # Compatibility 3 | 4 | 5 | ## Source Compatibility 6 | 7 | LSPServer has source compatibility with 12.0+ 8 | 9 | 10 | ## FrontEnd Compatibility 11 | 12 | Any source .wl files that have `(* ::Package::"Tags" *)` or `(* ::Code::Initialization::"Tags" *)` syntax may only be edited with a version 12.3+ FE 13 | 14 | 15 | ## Runtime Compatibility 16 | 17 | Building with Wolfram 12.0+ and running with the same version should always work, provided that all other dependencies are also built with the same version. 18 | 19 | Building and running with different Wolfram versions will not always work. 20 | 21 | Building with the latest Wolfram version (which is 13.1) is only guaranteed to work back to Wolfram 13.1+. 22 | 23 | This is due to various issues including LibraryLink versioning and rpath changes on MacOSX. 24 | 25 | 26 | ## WolframVersion 27 | 28 | WolframVersion in PacletInfo is 12.1+ to maintain the same minimum required version as CodeParser 29 | 30 | 31 | ## CCompilerDriver libraries 32 | 33 | This table show the value of -mmacosx-version-min for libraries generated by CCompilerDriver: 34 | 35 | | Wolfram version | MacOSX-x86-64 | MacOSX-ARM64 | 36 | | --------------- | ------------- | ------------ | 37 | | 12.0 | 10.10 | N/A | 38 | | 12.1 | 10.10 | N/A | 39 | | 12.2 | 10.12 | 11.0 | 40 | | 12.3 | 10.14 | 11.0 | 41 | | 13.0 | 10.14 | 11.0 | 42 | | 13.1 | 10.14 | 11.0 | 43 | | 13.2 | 10.15 | 11.0 | 44 | 45 | LSPServer sets the same macosx-version-min in order to achieve maximum compatibility with libraries generated by CCompilerDriver. 46 | 47 | 48 | ## Earlier Versions 49 | 50 | Manually modify WolframVersion in PacletInfo.wl to allow the paclet to be used. 51 | 52 | The message that you get when you install the paclet: 53 | ``` 54 | The paclet CodeParser was successfully installed. 55 | ``` 56 | does not necessarily mean that the paclet can be used. 57 | 58 | Make sure that the correct WolframVersion is specified. 59 | 60 | 61 | ## WolframLanguageSyntax Compatibility 62 | 63 | Only the latest released version of Wolfram System is supported. 64 | -------------------------------------------------------------------------------- /docs/diagnostics.md: -------------------------------------------------------------------------------- 1 | 2 | # Diagnostics 3 | 4 | ## Server 5 | 6 | Handle when LSPServer is not installed 7 | 8 | Right now, the command-line is: 9 | ``` 10 | Needs["LSPServer`"];LSPServer`StartServer[] 11 | ``` 12 | 13 | But what if LSPServer is not installed? 14 | 15 | 16 | Right now, kernel hangs after returning ``LSPServer`StartServer[]`` unevaluated, waiting for input 17 | 18 | 19 | Cannot simply do: 20 | ``` 21 | Needs["LSPServer`"];LSPServer`StartServer[];Exit[1] 22 | ``` 23 | because then I have no way of displaying error dialog to user. 24 | 25 | 26 | Could do: 27 | ``` 28 | Needs["LSPServer`"];LSPServer`StartServer[];Pause[20];Exit[1] 29 | ``` 30 | which will trigger timeout after 10 seconds and show dialog, but this is getting really ugly and esoteric. 31 | 32 | 33 | Could try to do: 34 | ``` 35 | Needs["LSPServer`"];LSPServer`StartServer[];Print["Content-Length: 64\r\n\r\n{\"jsonrpc\":\"2.0\",\"id\":0,\"error\":{\"code\":-32603,\"message\":\"XXX\"}}"];Exit[1]" 36 | ``` 37 | but this getting REALLY ugly now. 38 | 39 | 40 | ## Client 41 | 42 | I at least want to be able to kill the kernel process that is hanging when LSPServer is not installed. 43 | 44 | Clients should be able to do this. 45 | 46 | 47 | ## Sublime 48 | 49 | Resolution of sublime issue is that I should implement the can_start function of the API. 50 | 51 | This is not ideal for Wolfram, because an implementation of can_start basically needs to try to start a kernel itself. 52 | 53 | But implementing can_start is also complicated: 54 | 55 | Requires starting a separate kernel process, and now managing that process manually. 56 | 57 | Exiting this first kernel and starting the actual server kernel may expose race condition in license handling, may need to wait 1 second, etc. 58 | 59 | After paclets are installed, it would be silly to start a temp kernel for can_start every time, so need some kind of caching? 60 | 61 | Just so complicated. 62 | 63 | There is no direct way of killing kernel, and Sublime LSP developers do not really understand the issue that I was asking about. 64 | 65 | Related Sublime issue: 66 | https://github.com/sublimelsp/LSP/issues/1872 67 | 68 | 69 | 70 | ## VSCode 71 | 72 | A bug has been fixed in VSCode to allow clients to kill the server process. 73 | 74 | Related VSCode issue: 75 | https://github.com/microsoft/vscode-languageserver-node/issues/834 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /docs/notes.md: -------------------------------------------------------------------------------- 1 | 2 | ## implicit tokens 3 | 4 | displaying implicit tokens in LSP clients is EXPERIMENTAL. 5 | 6 | Specific tokens to be displayed can be controlled in settings: 7 | ``` 8 | "implicitTokens": [], 9 | ``` 10 | 11 | The possible values to use are: 12 | 13 | * `"*"`: display implicit Times character `×` 14 | * `","`: display `Null` around stray commas 15 | * `;;`: display `1` and `All` around `;;` 16 | * `;`: display `Null` after `;` 17 | * `?`: display `□` in place of missing arguments 18 | 19 | 20 | A good setting would be: 21 | ``` 22 | "implicitTokens": ["*", ",", ";;", "?"] 23 | ``` 24 | 25 | This will display most implicit tokens, but will NOT display Nulls after ; in CompoundExpressions. Those are annoying. 26 | 27 | You may also do: 28 | ``` 29 | "implicitTokens": ["*", ",", ";;", "?", ";"] 30 | ``` 31 | 32 | to see all implicit tokens. 33 | 34 | 35 | ### implicit token client language 36 | 37 | when these characters are sent to clients, they represent a combination of implicit tokens 38 | 39 | * `x`: implicit Times 40 | * `z`: implicit Times with space preceding it 41 | * `1`: implicit 1 42 | * `A`: implicit All 43 | * `N`: implicit Null 44 | * `e`: expected operand 45 | * `y`: implicit Times + implicit 1 46 | * `B`: implicit All + implicit Times 47 | * `C`: implicit All + implicit Times + implicit 1 48 | * `f`: expected operand + implicit Times 49 | * `D`: implicit All + implicit 1 50 | -------------------------------------------------------------------------------- /docs/sockets.md: -------------------------------------------------------------------------------- 1 | 2 | # Sublime Text (ST) 3 | 4 | ## Unsolved issues 5 | 6 | ### 1. Restart Servers 7 | 8 | Doing "LSP: Restart Servers" in ST returns "address already in use" errors. As ST tries to connect to the same port before the kernel closes down ST finds the port is already in use. 9 | 10 | Wolfram kernel takes few seconds to close down. Opening ST after kernel close restarts the server and works properly. 11 | 12 | ### 2. Timeout issue 13 | 14 | If LSPServer takes more than 5 seconds to launch Sublime returns "time out" error. 15 | 16 | * Timeout issue reported in [sublimelsp git](https://github.com/sublimelsp/LSP/issues/622). 17 | 18 | * Text from [Sublime Text Language server protocol documentation](https://lsp.readthedocs.io/en/latest/). 19 | 20 | > Set ```tcp_mode``` to "host", leave ```tcp_port``` unset for automatic port selection. ```tcp_port``` can be set if eg. debugging a server. You may want to check out the LSP source and extend the ```TCP_CONNECT_TIMEOUT```. 21 | 22 | * [Sublimelsp sourcecode for timeout](https://github.com/sublimelsp/LSP/blob/master/plugin/core/transports.py#L18). 23 | 24 | ## Important links 25 | * [Client-hosted tcp connection and client managing the socket life cycle](https://github.com/sublimelsp/LSP/issues/513) 26 | 27 | ## Setting for Socket based communication 28 | Settings for socket based communication is given below: 29 | 30 | ### When client opens the port 31 | 32 | Modify ```/Users/user-name/Library/Application Support/Sublime Text 3/Packages/User/LSP.sublime-settings``` file. We call this ```Socket``` mode of communication. 33 | 34 | We can modify this file from Sublime Text menu : ```Sublime Text > Preferences > Package Settings > LSP > Settings```. 35 | 36 | Setting for communication through ```Socket``` mode: 37 | 38 | ```{ 39 | "log_debug": true, 40 | "log_server": true, 41 | "log_stderr": true, 42 | "log_payloads": true, 43 | "clients": 44 | { 45 | "wolfram": 46 | { 47 | "enabled": true, 48 | "command": 49 | [ 50 | "C:\\Program Files\\Wolfram Research\\Mathematica\\12.1.1\\WolframKernel.exe", 51 | "-noinit", 52 | "-noprompt", 53 | "-nopaclet", 54 | "-noicon", 55 | "-run", 56 | "Needs[\"LSPServer`\"];LSPServer`StartServer[\"CommunicationMethod\" -> \"Socket\"]" 57 | ], 58 | "scopes": ["source.wolfram"], 59 | "syntaxes": "/Users/suman/Documents/WRI/External-Repo/Sublime-WolframLanguage/WolframLanguage.sublime-syntax", 60 | "languageId": "wolfram", 61 | "initializationOptions": { }, 62 | "tcp_mode": "host", 63 | "tcp_port": 5555 64 | } 65 | } 66 | } 67 | ``` 68 | We recommend using Mathematica version 12.1.1 for this mode. Although sometimes ST socket support works properly with later versions but we see the following issues to be fixed: 69 | 70 | * Server successfully connects to the socket opened by the client but never reads a message from the client. 71 | * Server fails to connect to the socket opened by the client and returns ```Failed socket operation```. 72 | 73 | 74 | ### When server opens the port 75 | 76 | Modify ```/Users/user-name/Library/Application Support/Sublime Text 3/Packages/User/LSP.sublime-settings``` file. We call this ```ListenSocket``` mode of communication. 77 | 78 | We can modify this file from Sublime Text menu : ```Sublime Text > Preferences > Package Settings > LSP > Settings```. 79 | 80 | Setting for communication through ```ListenSocket``` mode: 81 | 82 | ```{ 83 | "log_debug": true, 84 | "log_server": true, 85 | "log_stderr": true, 86 | "log_payloads": true, 87 | "clients": 88 | { 89 | "wolfram": 90 | { 91 | "enabled": true, 92 | "command": 93 | [ 94 | "C:\\Program Files\\Wolfram Research\\Mathematica\\12.1.1\\WolframKernel.exe", 95 | "-noinit", 96 | "-noprompt", 97 | "-nopaclet", 98 | "-noicon", 99 | "-run", 100 | "Needs[\"LSPServer`\"];LSPServer`StartServer[\"CommunicationMethod\" -> \"ListenSocket\"]" 101 | ], 102 | "scopes": ["source.wolfram"], 103 | "syntaxes": "/Users/suman/Documents/WRI/External-Repo/Sublime-WolframLanguage/WolframLanguage.sublime-syntax", 104 | "languageId": "wolfram", 105 | "initializationOptions": { }, 106 | "tcp_mode": "", 107 | "tcp_port": 5555 108 | } 109 | } 110 | } 111 | ``` 112 | This mode is used to support multi-client communication with single ```LSPServer```. 113 | 114 | ## Multiple clients support 115 | 116 | As communication through ```ListenSocket``` mode is possible in ST, ```LSPServer``` is capable of multiple client support. Currently this feature is working for ST in Mac-OS-X. Initial tests of this feature is done using three different instances of STs. We have seen all the three STs could communicate with the ```LSPServer```. 117 | 118 | ### I. Client setting for Multiple clients support 119 | 120 | All of the different STs should have the same setting as described in the sub-section: ```When server opens the port``` under ```Setting for Socket based communication```. 121 | 122 | ### II. Timing Tests 123 | 124 | Timing tests in the multi-client mode are done in `Sublime Text 3`. Settings and results of the timing tests are discussed below: 125 | 126 | #### Settings 127 | 128 | Three different instances of ST3s are open in the `ListenSocket` mode. All the different instances of STs started communicating with `LSPServer`. In each of the instances `Round Trip Timing Test` were done. 129 | 130 | #### results 131 | Typical `Round Trip Timing` data and correponding data range is shown here. No significant delay was observed while switching the window and getting responses from the `LSPServer`. 132 | __________________________________________________________ 133 | | | ST3-Window-1 | ST3-Window-2 | ST3-Window-3 | 134 | |--- | --- | --- | --- | 135 | |Round Trip Timing | 20 ms| 20 ms| 20 ms| 136 | |Data Range | 10 - 30 ms | 10 - 30 ms| 10 - 30 ms| 137 | __________________________________________________________ 138 | 139 | ### III. Issues 140 | * Socket timeout issue comes sometime especially in the slower machines. Restarting ST generally solve this issue. Improvement in ST4 may solve this issue. 141 | 142 | * All the different instances of the ST3 opens a WolframKernel. Only the first kernel lives. The second and third kernel closes down with error message: `Address already in use (code 48)`. So finally all the ST3 communicate through the kernel launched by the first ST3. 143 | 144 | TODO: If a WolframKernel is launched in the ListenSocket mode, we can try not to launch another kernel. 145 | 146 | * If any of the instances of the ST3 is closed then the kernel is shut down by the server. 147 | 148 | TODO: This issue needs to be fixed in the server side. 149 | 150 | 151 | # VSCode 152 | 153 | ## Important links 154 | 155 | ### Opening the port 156 | * [For connection timing issues the server is actually a client and the client is the server in terms of opening the ports.](https://github.com/microsoft/language-server-protocol/issues/604) 157 | 158 | 159 | ## Multiple clients support plans 160 | 161 | * [Plans for supporting multiple clients from a single LSP server?](https://github.com/microsoft/language-server-protocol/issues/1160) 162 | 163 | ## Setting for Socket based communication 164 | We are working to get Socket support for our VSCode plugin. 165 | -------------------------------------------------------------------------------- /scripts/re_build_LSPServer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | --------------------------------------------------------------------------------