├── .appveyor.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.binaries.txt
├── LICENSE.code.txt
├── README.md
├── bootstrap
├── .gitignore
├── dub.json
└── source
│ ├── dls
│ ├── bootstrap.d
│ └── util
│ │ ├── getopt.d
│ │ └── setup.d
│ └── main.d
├── data
├── .gitignore
└── data.d
├── dscanner.ini
├── dub.json
├── dub.selections.json
├── i18n
├── data
│ ├── translations.d
│ └── translations.json
├── dub.json
└── source
│ └── dls
│ └── util
│ └── i18n
│ ├── constants.d
│ └── package.d
├── protocol
├── dub.json
└── source
│ └── dls
│ └── protocol
│ ├── definitions.d
│ ├── errors.d
│ └── interfaces
│ ├── client.d
│ ├── general.d
│ ├── package.d
│ ├── text_document.d
│ ├── window.d
│ └── workspace.d
├── resources
└── icon.svg
├── source
├── dls
│ ├── info.d
│ ├── protocol
│ │ ├── handlers.d
│ │ ├── interfaces
│ │ │ ├── dls.d
│ │ │ └── other.d
│ │ ├── jsonrpc.d
│ │ ├── logger.d
│ │ ├── messages
│ │ │ ├── client.d
│ │ │ ├── general.d
│ │ │ ├── methods.d
│ │ │ ├── other.d
│ │ │ ├── text_document.d
│ │ │ ├── window.d
│ │ │ └── workspace.d
│ │ └── state.d
│ ├── server.d
│ ├── tools
│ │ ├── analysis_tool.d
│ │ ├── command_tool.d
│ │ ├── configuration.d
│ │ ├── format_tool
│ │ │ ├── internal
│ │ │ │ ├── builtin_format_tool.d
│ │ │ │ ├── builtin_format_visitor.d
│ │ │ │ ├── dfmt_format_tool.d
│ │ │ │ └── format_tool.d
│ │ │ └── package.d
│ │ ├── symbol_tool
│ │ │ ├── internal
│ │ │ │ ├── symbol_tool.d
│ │ │ │ └── symbol_visitor.d
│ │ │ └── package.d
│ │ └── tool.d
│ └── updater.d
└── main.d
├── tests
├── .gitattributes
├── .gitignore
├── formatting
│ ├── dub.json
│ ├── messages
│ │ ├── _order.txt
│ │ ├── initialize.in.json
│ │ ├── initialize.ref.json
│ │ ├── initialized.in.json
│ │ ├── initialized.ref.json
│ │ ├── shutdown.in.json
│ │ ├── shutdown.ref.json
│ │ ├── textDocument_formatting.in.json
│ │ ├── textDocument_formatting.ref.json
│ │ ├── textDocument_onTypeFormatting.in.json
│ │ ├── textDocument_onTypeFormatting.ref.json
│ │ ├── textDocument_rangeFormatting.in.json
│ │ └── textDocument_rangeFormatting.ref.json
│ └── source
│ │ └── app.d
├── hello
│ ├── dub.json
│ ├── messages
│ │ ├── _order.txt
│ │ ├── initialize.in.json
│ │ ├── initialize.ref.json
│ │ ├── initialized.in.json
│ │ ├── initialized.ref.json
│ │ ├── shutdown.in.json
│ │ └── shutdown.ref.json
│ └── source
│ │ └── app.d
├── main.d
├── references
│ ├── dub.json
│ ├── messages
│ │ ├── _order.txt
│ │ ├── initialize.in.json
│ │ ├── initialize.ref.json
│ │ ├── initialized.in.json
│ │ ├── initialized.ref.json
│ │ ├── shutdown.in.json
│ │ ├── shutdown.ref.json
│ │ ├── textDocument_references_1.in.json
│ │ ├── textDocument_references_1.ref.json
│ │ ├── textDocument_references_1_noDeclaration.in.json
│ │ ├── textDocument_references_1_noDeclaration.ref.json
│ │ ├── textDocument_references_2.in.json
│ │ ├── textDocument_references_2.ref.json
│ │ ├── textDocument_references_2_noDeclaration.in.json
│ │ ├── textDocument_references_2_noDeclaration.ref.json
│ │ ├── textDocument_references_3.in.json
│ │ ├── textDocument_references_3.ref.json
│ │ ├── textDocument_references_3_noDeclaration.in.json
│ │ ├── textDocument_references_3_noDeclaration.ref.json
│ │ ├── textDocument_references_4.in.json
│ │ ├── textDocument_references_4.ref.json
│ │ ├── textDocument_references_4_noDeclaration.in.json
│ │ └── textDocument_references_4_noDeclaration.ref.json
│ └── source
│ │ └── app.d
└── symbol
│ ├── dscanner.ini
│ ├── dub.json
│ ├── messages
│ ├── _order.txt
│ ├── initialize-documentSymbol.in.json
│ ├── initialize-documentSymbol.ref.json
│ ├── initialize-symbolInformation.in.json
│ ├── initialize-symbolInformation.ref.json
│ ├── initialized.in.json
│ ├── initialized.ref.json
│ ├── shutdown.in.json
│ ├── shutdown.ref.json
│ ├── textDocument_documentSymbol-documentSymbol.in.json
│ ├── textDocument_documentSymbol-documentSymbol.ref.json
│ ├── textDocument_documentSymbol-symbolInformation.in.json
│ ├── textDocument_documentSymbol-symbolInformation.ref.json
│ ├── workspace_symbol.in.json
│ └── workspace_symbol.ref.json
│ └── source
│ ├── app.d
│ └── lib.d
└── util
├── dub.json
└── source
└── dls
└── util
├── communicator.d
├── disposable_fiber.d
├── document.d
├── json.d
└── uri.d
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | platform: x64
2 | image: Visual Studio 2019
3 | environment:
4 | matrix:
5 | - ARCH: x86_64
6 | VC_ARCH: x64
7 | cache:
8 | - '%LOCALAPPDATA%\dub -> dub.selections.json'
9 | install:
10 | - ps: $latest = (Invoke-WebRequest "https://ldc-developers.github.io/LATEST").toString().trim()
11 | - ps: $url = "https://github.com/ldc-developers/ldc/releases/download/v$($latest)/ldc2-$($latest)-windows-$($env:VC_ARCH).7z";
12 | - ps: Push-Location "$($env:TMP)"
13 | - ps: Invoke-WebRequest $url -OutFile ".\ldc.7z"
14 | - ps: 7z x ldc.7z > $null
15 | - ps: Pop-Location
16 | - ps: $env:PATH += ";$($env:TMP)\ldc2-$($latest)-windows-$($env:VC_ARCH)\bin"
17 | - call msvcEnv %VC_ARCH%
18 | build_script:
19 | - if defined APPVEYOR_REPO_TAG_NAME (set BUILD=release) else (set BUILD=debug)
20 | - dub build --arch=%ARCH% --compiler=ldc2 --build=%BUILD%
21 | after_build:
22 | - if defined APPVEYOR_REPO_TAG_NAME (set TAG_NAME=%APPVEYOR_REPO_TAG_NAME%) else (set TAG_NAME=untagged)
23 | - 7z -mx=9 a dls-%TAG_NAME%.windows.%ARCH%.zip dls.exe LICENSE.txt
24 | test_script:
25 | - dub test --arch=%ARCH% --compiler=ldc2 --main-file=tests\main.d
26 | artifacts:
27 | - path: dls-*.zip
28 | deploy:
29 | - provider: GitHub
30 | auth_token: $(GITHUB_API_KEY)
31 | artifact: /dls-.*\.zip/
32 | on:
33 | appveyor_repo_tag: true
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/d
3 |
4 | ### D ###
5 | # Compiled Object files
6 | *.o
7 | *.obj
8 |
9 | # Compiled Dynamic libraries
10 | *.so
11 | *.dylib
12 | *.dll
13 |
14 | # Compiled Static libraries
15 | *.a
16 | *.lib
17 |
18 | # Executables
19 | *.exe
20 |
21 | # DUB
22 | .dub
23 | docs.json
24 | __dummy.html
25 | docs/
26 |
27 | # Code coverage
28 | *.lst
29 |
30 |
31 | # End of https://www.gitignore.io/api/d
32 |
33 | /dls
34 | /dls-test-library
35 | /resources/icon.png
36 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | dist: xenial
3 | language: d
4 | matrix:
5 | fast_finish: true
6 | include:
7 | - d: ldc
8 | os: linux
9 | env: ARCH=x86_64
10 | - d: ldc
11 | os: osx
12 | env: ARCH=x86_64
13 | - d: dmd-beta
14 | os: linux
15 | - d: ldc-beta
16 | os: linux
17 | allow_failures:
18 | - d: dmd-beta
19 | - d: ldc-beta
20 | cache:
21 | directories:
22 | - $HOME/.dub
23 | before_script:
24 | - ln -s /usr/bin/ld.gold ~/ld
25 | script:
26 | - if [[ -n $TRAVIS_TAG ]]; then export BUILD=release; export OP=build; fi
27 | - export PATH="$HOME:$PATH"
28 | - dub ${OP:-test --main-file=tests/main.d} --arch=${ARCH:-x86_64} --compiler=$DC --build=${BUILD:-debug}
29 | before_deploy:
30 | - if which strip; then strip dls; fi
31 | - zip -9 dls-${TRAVIS_TAG:-untagged}.$TRAVIS_OS_NAME.$ARCH.zip dls LICENSE.txt
32 | - export TARGET_COMMITISH="$(echo -n release/$TRAVIS_TAG | sed 's/.[0-9]*$/.x/')"
33 | deploy:
34 | provider: releases
35 | api_key: "$GITHUB_API_KEY"
36 | target_commitish: "$TARGET_COMMITISH"
37 | file_glob: true
38 | file: dls-*.zip
39 | skip_cleanup: true
40 | on:
41 | condition: "-n $ARCH"
42 | repo: d-language-server/dls
43 | tags: true
44 |
--------------------------------------------------------------------------------
/LICENSE.code.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2021 Laurent Tréguier
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/bootstrap/.gitignore:
--------------------------------------------------------------------------------
1 | /dls_bootstrap
2 |
--------------------------------------------------------------------------------
/bootstrap/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bootstrap",
3 | "authors": [
4 | "Laurent Tréguier"
5 | ],
6 | "description": "DLS bootstrap utility",
7 | "copyright": "Copyright © 2018, Laurent Tréguier",
8 | "license": "GPL-3.0 or later",
9 | "dependencies": {
10 | "dls:i18n": "*"
11 | },
12 | "libs-windows": [
13 | "wininet"
14 | ]
15 | }
--------------------------------------------------------------------------------
/bootstrap/source/dls/util/getopt.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.util.getopt;
22 |
23 | import std.getopt : Option;
24 |
25 | void printHelp(const char[] title, const Option[] options, void delegate(const char[]) sink)
26 | {
27 | import std.ascii : newline;
28 | import std.algorithm : map, maxElement;
29 |
30 | sink(title);
31 | sink(newline);
32 |
33 | immutable longest = maxElement(options.map!(o => o.optLong.length + (o.optShort.length > 0
34 | ? o.optShort.length + 1 : 0)));
35 |
36 | foreach (option; options)
37 | {
38 | auto lineSize = option.optLong.length;
39 | sink(option.optLong);
40 |
41 | if (option.optShort.length > 0)
42 | {
43 | lineSize += option.optShort.length + 1;
44 | sink("|");
45 | sink(option.optShort);
46 | }
47 |
48 | if (option.help.length > 0)
49 | {
50 | auto spaces = new char[longest + 1 - lineSize];
51 | spaces[] = ' ';
52 | sink(spaces);
53 | sink(option.help);
54 | }
55 |
56 | sink(newline);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/bootstrap/source/dls/util/setup.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.util.setup;
22 |
23 | void initialSetup()
24 | {
25 | version (Windows)
26 | {
27 | import std.algorithm : splitter;
28 | import std.file : exists;
29 | import std.path : buildNormalizedPath, dirName;
30 | import std.process : environment;
31 |
32 | version (X86_64)
33 | {
34 | enum binDir = "bin64";
35 | }
36 | else
37 | {
38 | enum binDir = "bin";
39 | }
40 |
41 | auto pathParts = splitter(environment["PATH"], ';');
42 |
43 | foreach (path; pathParts)
44 | {
45 | if (exists(buildNormalizedPath(path, "dmd.exe")))
46 | {
47 | environment["PATH"] = buildNormalizedPath(dirName(path), binDir)
48 | ~ ';' ~ environment["PATH"];
49 | return;
50 | }
51 | }
52 |
53 | foreach (path; pathParts)
54 | {
55 | if (exists(buildNormalizedPath(path, "ldc2.exe")))
56 | {
57 | environment["PATH"] = path ~ ';' ~ environment["PATH"];
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/bootstrap/source/main.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | shared static this()
22 | {
23 | import dls.util.setup : initialSetup;
24 |
25 | initialSetup();
26 | }
27 |
28 | int main(string[] args)
29 | {
30 | import dls.bootstrap : canDownloadDls, downloadDls, linkDls;
31 | import dls.util.i18n : Tr, tr;
32 | import dls.util.getopt : printHelp;
33 | import std.ascii : newline;
34 | import std.conv : text;
35 | import std.file : thisExePath;
36 | import std.format : format;
37 | import std.getopt : config, getopt;
38 | import std.path : dirName;
39 | import std.stdio : stderr, stdout;
40 |
41 | bool check;
42 | bool localization;
43 | bool progress;
44 |
45 | try
46 | {
47 | //dfmt off
48 | auto info = getopt(args, config.passThrough,
49 | "check|c",
50 | tr(Tr.bootstrap_help_check),
51 | &check,
52 | "localization|l",
53 | tr(Tr.bootstrap_help_localization),
54 | &localization,
55 | "progress|p",
56 | format(tr(Tr.bootstrap_help_progress)),
57 | &progress);
58 | //dfmt on
59 |
60 | if (info.helpWanted)
61 | {
62 | printHelp(tr(Tr.bootstrap_help_title), info.options, data => stdout.rawWrite(data));
63 | return 0;
64 | }
65 | }
66 | catch (Exception e)
67 | {
68 | stderr.writeln(e.msg);
69 | return 1;
70 | }
71 |
72 | string output;
73 | int status;
74 |
75 | if (check)
76 | {
77 | immutable ok = canDownloadDls;
78 | output = text(ok);
79 | status = ok ? 0 : 1;
80 | }
81 | else
82 | {
83 | if (localization)
84 | {
85 | stderr.rawWrite("installing:" ~ tr(Tr.bootstrap_installDls_installing) ~ '\t');
86 | stderr.rawWrite("downloading:" ~ tr(Tr.bootstrap_installDls_downloading) ~ '\t');
87 | stderr.rawWrite("extracting:" ~ tr(Tr.bootstrap_installDls_extracting));
88 | stderr.rawWrite(newline);
89 | stderr.flush();
90 | }
91 |
92 | immutable printSize = progress ? (size_t size) {
93 | stderr.rawWrite(text(size));
94 | stderr.rawWrite(newline);
95 | stderr.flush();
96 | } : null;
97 | immutable printExtract = progress ? () {
98 | stderr.rawWrite("extract");
99 | stderr.rawWrite(newline);
100 | stderr.flush();
101 | } : null;
102 |
103 | downloadDls(printSize, printSize, printExtract);
104 | output = linkDls();
105 | }
106 |
107 | stdout.rawWrite(output);
108 | stdout.rawWrite(newline);
109 | return status;
110 | }
111 |
--------------------------------------------------------------------------------
/data/.gitignore:
--------------------------------------------------------------------------------
1 | /*.txt
--------------------------------------------------------------------------------
/data/data.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | void main()
22 | {
23 | import std.file : exists, readText, write;
24 | import std.path : buildNormalizedPath;
25 | import std.process : environment;
26 |
27 | immutable dataPath = buildNormalizedPath(environment["DUB_PACKAGE_DIR"], "data");
28 | immutable fileFillers = [
29 | "dls-version.txt" : environment.get("DUB_PACKAGE_VERSION", getVersionFromDescription()),
30 | "build-platform.txt" : environment.get("DUB_PLATFORM", "unknown-build-platform"),
31 | "build-arch.txt" : environment.get("DUB_ARCH", "unknown-build-arch"),
32 | "build-type.txt" : environment.get("DUB_BUILD_TYPE", "unknown-build-type"),
33 | "compiler-version.txt" : getCompilerVersion()
34 | ];
35 |
36 | foreach (file, newContent; fileFillers)
37 | {
38 | immutable dataFile = buildNormalizedPath(dataPath, file);
39 | immutable oldContent = exists(dataFile) ? readText(dataFile) : "";
40 |
41 | if (newContent != oldContent)
42 | {
43 | write(dataFile, newContent);
44 | }
45 | }
46 | }
47 |
48 | string getCompilerVersion()
49 | {
50 | import std.process : environment, execute;
51 | import std.regex : matchFirst, regex;
52 |
53 | return execute([environment["DC"], "--version"]).output.matchFirst(regex(`\d+\.\d+\.\d+`)).front;
54 | }
55 |
56 | string getVersionFromDescription()
57 | {
58 | import std.algorithm : find;
59 | import std.json : parseJSON;
60 | import std.process : Config, environment, execute;
61 |
62 | immutable describe = execute(["dub", "describe"], null, Config.none, size_t.max, environment["DUB_PACKAGE_DIR"]);
63 | immutable desc = parseJSON(describe.output);
64 | return desc["packages"].array.find!(p => p["name"] == desc["rootPackage"])[0]["version"].str;
65 | }
66 |
--------------------------------------------------------------------------------
/dscanner.ini:
--------------------------------------------------------------------------------
1 | [analysis.config.StaticAnalysisConfig]
2 | undocumented_declaration_check="disabled"
--------------------------------------------------------------------------------
/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dls",
3 | "authors": [
4 | "Laurent Tréguier"
5 | ],
6 | "description": "D Language Server",
7 | "copyright": "Copyright © 2018, Laurent Tréguier",
8 | "license": "GPL-3.0 or later",
9 | "stringImportPaths": [
10 | "data"
11 | ],
12 | "dependencies": {
13 | "dls:bootstrap": "*",
14 | "dls:i18n": "*",
15 | "dls:protocol": "*",
16 | "dls:util": "*",
17 | "dub": "~>1.20.0",
18 | "dcd": "~>0.12.0",
19 | "dscanner": "~>0.8.0",
20 | "dfmt": "~>0.11.0"
21 | },
22 | "versions": [
23 | "StdLoggerDisableFatal"
24 | ],
25 | "preGenerateCommands": [
26 | "dmd $PACKAGE_DIR/data/data.d -of=$PACKAGE_DIR/data/data.exe || ldc2 $PACKAGE_DIR/data/data.d -of=$PACKAGE_DIR/data/data.exe || gdc $PACKAGE_DIR/data/data.d -o $PACKAGE_DIR/data/data.exe",
27 | "$PACKAGE_DIR/data/data.exe"
28 | ],
29 | "subPackages": [
30 | "bootstrap",
31 | "i18n",
32 | "protocol",
33 | "util"
34 | ]
35 | }
--------------------------------------------------------------------------------
/dub.selections.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileVersion": 1,
3 | "versions": {
4 | "dcd": "0.12.0",
5 | "dfmt": "0.11.0",
6 | "dscanner": "0.8.0",
7 | "dsymbol": "0.9.2",
8 | "dub": "1.20.0",
9 | "emsi_containers": "0.8.0-alpha.19",
10 | "inifiled": "1.3.1",
11 | "libddoc": "0.7.1",
12 | "libdparse": "0.13.2",
13 | "msgpack-d": "1.0.2",
14 | "stdx-allocator": "2.77.5"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/i18n/data/translations.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | private immutable dubPackageDir = "DUB_PACKAGE_DIR";
22 |
23 | void main()
24 | {
25 | import std.algorithm : sort;
26 | import std.ascii : newline;
27 | import std.file : readText, write;
28 | import std.format : format;
29 | import std.json : parseJSON;
30 | import std.path : asRelativePath, buildNormalizedPath;
31 | import std.process : environment;
32 | import std.range : replace;
33 |
34 | immutable packageDir = environment[dubPackageDir];
35 | immutable translationsPath = buildNormalizedPath(packageDir, "data", "translations.json");
36 | immutable trModulePath = buildNormalizedPath(packageDir, "source", "dls",
37 | "util", "i18n", "constants.d");
38 | immutable trModuleContent = readText(trModulePath);
39 | auto translations = parseJSON(readText(translationsPath));
40 | string content;
41 |
42 | content ~= format(q{/+ This file is generated automatically by %s.d +/}, __MODULE__);
43 | content ~= newline ~ newline;
44 | content ~= q{module dls.util.i18n.constants;};
45 | content ~= newline ~ newline;
46 | content ~= q{enum Tr : string};
47 | content ~= newline;
48 | content ~= "{";
49 | content ~= newline;
50 | content ~= q{ _ = "### BAD TRANSLATION KEY ###",};
51 | content ~= newline;
52 |
53 | foreach (key; sort(translations.object.keys))
54 | {
55 | content ~= format(" %s = \"%s\"%s" ~ newline, key.replace(".", "_"), key, ",");
56 | }
57 |
58 | content ~= "}";
59 | content ~= newline;
60 |
61 | if (content != trModuleContent)
62 | {
63 | write(trModulePath, content);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/i18n/data/translations.json:
--------------------------------------------------------------------------------
1 | {
2 | "app.help.title": {
3 | "en": "D Language Server",
4 | "fr": "Serveur de Language D"
5 | },
6 | "app.help.version": {
7 | "en": "Display program version",
8 | "fr": "Afficher la version du programme"
9 | },
10 | "app.help.stdio": {
11 | "en": "Use standard input and output streams",
12 | "fr": "Utiliser l'entrée et la sortie standards"
13 | },
14 | "app.help.socket": {
15 | "en": "Value: Connect to a socket on a specified port",
16 | "fr": "Valeur: Se connecter à une socket sur le port spécifié"
17 | },
18 | "app.version.dlsVersion": {
19 | "en": "DLS version $1 [$2, $3, $4, $5]",
20 | "fr": "DLS version $1 [$2, $3, $4, $5]"
21 | },
22 | "app.version.compilerVersion": {
23 | "en": "Compiled with $1 $2 (DMDFE: $3)",
24 | "fr": "Compilé avec $1 $2 (DMDFE: $3)"
25 | },
26 | "app.rpc.errorCodes.parseError": {
27 | "en": "Parse error",
28 | "fr": "Erreur d'analyse"
29 | },
30 | "app.rpc.errorCodes.invalidRequest": {
31 | "en": "Invalid request",
32 | "fr": "Requête invalide"
33 | },
34 | "app.rpc.errorCodes.methodNotFound": {
35 | "en": "Method not found",
36 | "fr": "Méthode non trouvée"
37 | },
38 | "app.rpc.errorCodes.invalidParams": {
39 | "en": "Invalid parameters",
40 | "fr": "paramètres invalides"
41 | },
42 | "app.rpc.errorCodes.internalError": {
43 | "en": "Internal error",
44 | "fr": "Erreur interne"
45 | },
46 | "app.rpc.errorCodes.serverNotInitialized": {
47 | "en": "Server not initialized",
48 | "fr": "Serveur non initialisé"
49 | },
50 | "app.rpc.errorCodes.unknownErrorCode": {
51 | "en": "Unknown error code",
52 | "fr": "Code d'erreur inconnu"
53 | },
54 | "app.rpc.errorCodes.requestCancelled": {
55 | "en": "Request cancelled",
56 | "fr": "Requête annulée"
57 | },
58 | "app.upgradeSelections": {
59 | "en": "Dependencies for project $1 changed",
60 | "fr": "Dépendences du projet $1 modifiées"
61 | },
62 | "app.upgradeSelections.upgrade": {
63 | "en": "Upgrade selections",
64 | "fr": "Mettre à jour les sélections"
65 | },
66 | "app.upgradeSelections.upgrading": {
67 | "en": "Upgrading selections",
68 | "fr": "Mise à jour des sélections"
69 | },
70 | "app.upgradeSelections.error": {
71 | "_type": 1,
72 | "en": "$1",
73 | "fr": "$1"
74 | },
75 | "app.upgradeDls": {
76 | "en": "DLS version $1 is available (current version: $2)",
77 | "fr": "DLS version $1 est disponible (version actuelle: $2)"
78 | },
79 | "app.upgradeDls.upgrade": {
80 | "en": "Upgrade",
81 | "fr": "Mettre à jour"
82 | },
83 | "app.upgradeDls.upgrading": {
84 | "en": "Upgrading DLS",
85 | "fr": "Mise à jour de DLS"
86 | },
87 | "app.upgradeDls.downloading": {
88 | "en": "Downloading",
89 | "fr": "Téléchargement"
90 | },
91 | "app.upgradeDls.extracting": {
92 | "en": "Extracting",
93 | "fr": "Extraction"
94 | },
95 | "app.upgradeDls.downloadError": {
96 | "en": "DLS could not be downloaded",
97 | "fr": "DLS n'a pas pu être téléchargé"
98 | },
99 | "app.upgradeDls.linkError": {
100 | "_type": 1,
101 | "en": "The symlink to DLS could not br created",
102 | "fr": "Le lien symbolique vers DLS n'a pas pu être créé"
103 | },
104 | "app.showChangelog": {
105 | "en": "DLS upgraded to version $1",
106 | "fr": "DLS mis à jour vers la version $1"
107 | },
108 | "app.showChangelog.show": {
109 | "en": "See what's new",
110 | "fr": "Voir les nouveautés"
111 | },
112 | "app.command.diagnostic.disableCheck.local": {
113 | "en": "Disable $1 (this line)",
114 | "fr": "Désactiver $1 (cette ligne)"
115 | },
116 | "app.command.diagnostic.disableCheck.global": {
117 | "en": "Disable $1 (project-wide)",
118 | "fr": "Désactiver $1 (tout le projet)"
119 | },
120 | "bootstrap.help.title": {
121 | "en": "DLS installer",
122 | "fr": "Installateur de DLS"
123 | },
124 | "bootstrap.help.default": {
125 | "en": "default",
126 | "fr": "défaut"
127 | },
128 | "bootstrap.help.check": {
129 | "en": "Checks if the selected method is available.",
130 | "fr": "Vérifie si la méthode sélectionnée est disponible."
131 | },
132 | "bootstrap.help.localization": {
133 | "en": "Output localization strings.",
134 | "fr": "Émettre des chaînes pour la localisation."
135 | },
136 | "bootstrap.help.progress": {
137 | "en": "Show progress.",
138 | "fr": "Montrer le progrès."
139 | },
140 | "bootstrap.installDls.installing": {
141 | "en": "Installing DLS",
142 | "fr": "Installation de DLS"
143 | },
144 | "bootstrap.installDls.downloading": {
145 | "en": "Downloading",
146 | "fr": "Téléchargement"
147 | },
148 | "bootstrap.installDls.extracting": {
149 | "en": "Extracting",
150 | "fr": "Extraction"
151 | }
152 | }
--------------------------------------------------------------------------------
/i18n/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "i18n",
3 | "authors": [
4 | "Laurent Tréguier"
5 | ],
6 | "description": "DLS translation utilities",
7 | "copyright": "Copyright © 2018, Laurent Tréguier",
8 | "license": "GPL-3.0 or later",
9 | "stringImportPaths": [
10 | "data"
11 | ],
12 | "dependencies": {
13 | "dls:protocol": "*"
14 | },
15 | "preGenerateCommands": [
16 | "dmd $PACKAGE_DIR/data/translations.d -of=$PACKAGE_DIR/data/translations.exe || ldc2 $PACKAGE_DIR/data/translations.d -of=$PACKAGE_DIR/data/translations.exe || gdc $PACKAGE_DIR/data/translations.d -o $PACKAGE_DIR/data/translations.exe",
17 | "$PACKAGE_DIR/data/translations.exe"
18 | ]
19 | }
--------------------------------------------------------------------------------
/i18n/source/dls/util/i18n/constants.d:
--------------------------------------------------------------------------------
1 | /+ This file is generated automatically by translations.d +/
2 |
3 | module dls.util.i18n.constants;
4 |
5 | enum Tr : string
6 | {
7 | _ = "### BAD TRANSLATION KEY ###",
8 | app_command_diagnostic_disableCheck_global = "app.command.diagnostic.disableCheck.global",
9 | app_command_diagnostic_disableCheck_local = "app.command.diagnostic.disableCheck.local",
10 | app_help_socket = "app.help.socket",
11 | app_help_stdio = "app.help.stdio",
12 | app_help_title = "app.help.title",
13 | app_help_version = "app.help.version",
14 | app_rpc_errorCodes_internalError = "app.rpc.errorCodes.internalError",
15 | app_rpc_errorCodes_invalidParams = "app.rpc.errorCodes.invalidParams",
16 | app_rpc_errorCodes_invalidRequest = "app.rpc.errorCodes.invalidRequest",
17 | app_rpc_errorCodes_methodNotFound = "app.rpc.errorCodes.methodNotFound",
18 | app_rpc_errorCodes_parseError = "app.rpc.errorCodes.parseError",
19 | app_rpc_errorCodes_requestCancelled = "app.rpc.errorCodes.requestCancelled",
20 | app_rpc_errorCodes_serverNotInitialized = "app.rpc.errorCodes.serverNotInitialized",
21 | app_rpc_errorCodes_unknownErrorCode = "app.rpc.errorCodes.unknownErrorCode",
22 | app_showChangelog = "app.showChangelog",
23 | app_showChangelog_show = "app.showChangelog.show",
24 | app_upgradeDls = "app.upgradeDls",
25 | app_upgradeDls_downloadError = "app.upgradeDls.downloadError",
26 | app_upgradeDls_downloading = "app.upgradeDls.downloading",
27 | app_upgradeDls_extracting = "app.upgradeDls.extracting",
28 | app_upgradeDls_linkError = "app.upgradeDls.linkError",
29 | app_upgradeDls_upgrade = "app.upgradeDls.upgrade",
30 | app_upgradeDls_upgrading = "app.upgradeDls.upgrading",
31 | app_upgradeSelections = "app.upgradeSelections",
32 | app_upgradeSelections_error = "app.upgradeSelections.error",
33 | app_upgradeSelections_upgrade = "app.upgradeSelections.upgrade",
34 | app_upgradeSelections_upgrading = "app.upgradeSelections.upgrading",
35 | app_version_compilerVersion = "app.version.compilerVersion",
36 | app_version_dlsVersion = "app.version.dlsVersion",
37 | bootstrap_help_check = "bootstrap.help.check",
38 | bootstrap_help_default = "bootstrap.help.default",
39 | bootstrap_help_localization = "bootstrap.help.localization",
40 | bootstrap_help_progress = "bootstrap.help.progress",
41 | bootstrap_help_title = "bootstrap.help.title",
42 | bootstrap_installDls_downloading = "bootstrap.installDls.downloading",
43 | bootstrap_installDls_extracting = "bootstrap.installDls.extracting",
44 | bootstrap_installDls_installing = "bootstrap.installDls.installing",
45 | }
46 |
--------------------------------------------------------------------------------
/i18n/source/dls/util/i18n/package.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.util.i18n;
22 |
23 | public import dls.util.i18n.constants;
24 | import dls.protocol.interfaces : MessageType;
25 | import std.json : JSONValue;
26 |
27 | private enum translationsJson = import("translations.json");
28 | private immutable JSONValue translations;
29 | private immutable string locale;
30 | private immutable defaultLocale = "en";
31 |
32 | shared static this()
33 | {
34 | import std.json : parseJSON;
35 |
36 | translations = parseJSON(translationsJson);
37 | locale = defaultLocale;
38 |
39 | version (Windows)
40 | {
41 | import core.sys.windows.windef : DWORD, ERROR_SUCCESS, LONG, HKEY;
42 | import core.sys.windows.winnt : KEY_READ;
43 | import core.sys.windows.winreg : HKEY_USERS, RegOpenKeyExA, RegQueryValueExA;
44 | import std.string : toStringz;
45 |
46 | HKEY hKey;
47 | auto regKeyCStr = toStringz(`.DEFAULT\Control Panel\International`);
48 |
49 | if (RegOpenKeyExA(HKEY_USERS, regKeyCStr, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
50 | {
51 | return;
52 | }
53 |
54 | DWORD size = 32;
55 | auto buffer = new char[size];
56 | auto localeNameCStr = toStringz("LocaleName");
57 |
58 | if (RegQueryValueExA(hKey, localeNameCStr, null, null, buffer.ptr, &size) != ERROR_SUCCESS)
59 | {
60 | return;
61 | }
62 |
63 | if (size >= 2)
64 | {
65 | locale = buffer[0 .. 2].idup;
66 | }
67 | }
68 | else version (Posix)
69 | {
70 | import std.process : environment;
71 |
72 | auto lang = environment.get("LANG", defaultLocale);
73 |
74 | if (lang.length >= 2)
75 | {
76 | locale = lang[0 .. 2];
77 | }
78 | }
79 | else
80 | {
81 | locale = defaultLocale;
82 | }
83 | }
84 |
85 | string tr(Tr identifier, string[] args = [])
86 | {
87 | import std.ascii : newline;
88 | import std.conv : text;
89 | import std.range : replace;
90 |
91 | auto message = translations[identifier];
92 | auto localizedMessage = message[locale in message ? locale : defaultLocale].str;
93 |
94 | foreach (i, arg; args)
95 | {
96 | localizedMessage = localizedMessage.replace('$' ~ text(i + 1), arg);
97 | }
98 |
99 | return localizedMessage.replace("$n", newline);
100 | }
101 |
102 | MessageType trType(Tr message)
103 | {
104 | auto t = translations[message];
105 | return "_type" in t ? cast(MessageType) t["_type"].integer : MessageType.info;
106 | }
107 |
--------------------------------------------------------------------------------
/protocol/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "protocol",
3 | "authors": [
4 | "Laurent Tréguier"
5 | ],
6 | "description": "LSP interfaces implementations",
7 | "copyright": "Copyright © 2018, Laurent Tréguier",
8 | "license": "GPL-3.0 or later"
9 | }
--------------------------------------------------------------------------------
/protocol/source/dls/protocol/errors.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.errors;
22 |
23 | class InvalidParamsException : Exception
24 | {
25 | this(string msg)
26 | {
27 | super("Invalid parameters: " ~ msg);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/protocol/source/dls/protocol/interfaces/client.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.interfaces.client;
22 |
23 | private abstract class RegistrationBase
24 | {
25 | string id;
26 | string method;
27 |
28 | @safe this(string id, string method) pure nothrow
29 | {
30 | this.id = id;
31 | this.method = method;
32 | }
33 | }
34 |
35 | package interface RegistrationOptions
36 | {
37 | }
38 |
39 | final class Registration(R : RegistrationOptions) : RegistrationBase
40 | {
41 | import std.typecons : Nullable;
42 |
43 | Nullable!R registerOptions;
44 |
45 | @safe this(string id = string.init, string method = string.init,
46 | Nullable!R registerOptions = Nullable!R.init) pure nothrow
47 | {
48 | super(id, method);
49 | this.registerOptions = registerOptions;
50 | }
51 | }
52 |
53 | class TextDocumentRegistrationOptions : RegistrationOptions
54 | {
55 | import dls.protocol.definitions : DocumentSelector;
56 | import std.typecons : Nullable;
57 |
58 | Nullable!DocumentSelector documentSelector;
59 |
60 | @safe this(Nullable!DocumentSelector documentSelector = Nullable!DocumentSelector.init) pure nothrow
61 | {
62 | this.documentSelector = documentSelector;
63 | }
64 | }
65 |
66 | final class RegistrationParams(R : RegistrationOptions)
67 | {
68 | Registration!R[] registrations;
69 |
70 | @safe this(Registration!R[] registrations = Registration!R[].init) pure nothrow
71 | {
72 | this.registrations = registrations;
73 | }
74 | }
75 |
76 | final class Unregistration : RegistrationBase
77 | {
78 | @safe this(string id = string.init, string method = string.init) pure nothrow
79 | {
80 | super(id, method);
81 | }
82 | }
83 |
84 | final class UnregistrationParams
85 | {
86 | Unregistration[] unregistrations;
87 |
88 | @safe this(Unregistration[] unregistrations = Unregistration[].init) pure nothrow
89 | {
90 | this.unregistrations = unregistrations;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/protocol/source/dls/protocol/interfaces/package.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.interfaces;
22 |
23 | public import dls.protocol.interfaces.client;
24 | public import dls.protocol.interfaces.general;
25 | public import dls.protocol.interfaces.text_document;
26 | public import dls.protocol.interfaces.window;
27 | public import dls.protocol.interfaces.workspace;
28 |
--------------------------------------------------------------------------------
/protocol/source/dls/protocol/interfaces/window.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.interfaces.window;
22 |
23 | class ShowMessageParams
24 | {
25 | MessageType type;
26 | string message;
27 |
28 | @safe this(MessageType type = MessageType.init, string message = string.init) pure nothrow
29 | {
30 | this.type = type;
31 | this.message = message;
32 | }
33 | }
34 |
35 | enum MessageType : ubyte
36 | {
37 | error = 1,
38 | warning = 2,
39 | info = 3,
40 | log = 4
41 | }
42 |
43 | final class ShowMessageRequestParams : ShowMessageParams
44 | {
45 | import std.typecons : Nullable;
46 |
47 | Nullable!(MessageActionItem[]) actions;
48 |
49 | @safe this(MessageType type = MessageType.init, string message = string.init,
50 | Nullable!(MessageActionItem[]) actions = Nullable!(MessageActionItem[]).init) pure nothrow
51 | {
52 | super(type, message);
53 | this.actions = actions;
54 | }
55 | }
56 |
57 | final class MessageActionItem
58 | {
59 | string title;
60 |
61 | @safe this(string title = string.init) pure nothrow
62 | {
63 | this.title = title;
64 | }
65 | }
66 |
67 | alias LogMessageParams = ShowMessageParams;
68 |
--------------------------------------------------------------------------------
/protocol/source/dls/protocol/interfaces/workspace.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.interfaces.workspace;
22 |
23 | import dls.protocol.interfaces.client : RegistrationOptions;
24 |
25 | final class WorkspaceFolder
26 | {
27 | import dls.protocol.definitions : DocumentUri;
28 |
29 | DocumentUri uri;
30 | string name;
31 |
32 | @safe this() pure nothrow
33 | {
34 | }
35 | }
36 |
37 | final class DidChangeWorkspaceFoldersParams
38 | {
39 | WorkspaceFoldersChangeEvent event;
40 |
41 | @safe this() pure nothrow
42 | {
43 | }
44 | }
45 |
46 | final class WorkspaceFoldersChangeEvent
47 | {
48 | WorkspaceFolder[] added;
49 | WorkspaceFolder[] removed;
50 |
51 | @safe this() pure nothrow
52 | {
53 | }
54 | }
55 |
56 | final class DidChangeConfigurationParams
57 | {
58 | import std.json : JSONValue;
59 |
60 | JSONValue settings;
61 |
62 | @safe this() pure nothrow
63 | {
64 | }
65 | }
66 |
67 | final class ConfigurationParams
68 | {
69 | ConfigurationItem[] items;
70 |
71 | @safe this(ConfigurationItem[] items = ConfigurationItem[].init) pure nothrow
72 | {
73 | this.items = items;
74 | }
75 | }
76 |
77 | final class ConfigurationItem
78 | {
79 | import dls.protocol.definitions : DocumentUri;
80 | import std.typecons : Nullable;
81 |
82 | Nullable!DocumentUri scopeUri;
83 | Nullable!string section;
84 |
85 | @safe this(Nullable!DocumentUri scopeUri = Nullable!DocumentUri.init,
86 | Nullable!string section = Nullable!string.init) pure nothrow
87 | {
88 | this.scopeUri = scopeUri;
89 | this.section = section;
90 | }
91 | }
92 |
93 | final class DidChangeWatchedFilesParams
94 | {
95 | FileEvent[] changes;
96 |
97 | @safe this() pure nothrow
98 | {
99 | }
100 | }
101 |
102 | final class FileEvent
103 | {
104 | import dls.protocol.definitions : DocumentUri;
105 |
106 | DocumentUri uri;
107 | FileChangeType type;
108 |
109 | @safe this() pure nothrow
110 | {
111 | }
112 | }
113 |
114 | enum FileChangeType : ubyte
115 | {
116 | created = 1,
117 | changed = 2,
118 | deleted = 3
119 | }
120 |
121 | final class DidChangeWatchedFilesRegistrationOptions : RegistrationOptions
122 | {
123 | FileSystemWatcher[] watchers;
124 |
125 | @safe this(FileSystemWatcher[] watchers = FileSystemWatcher[].init) pure nothrow
126 | {
127 | this.watchers = watchers;
128 | }
129 | }
130 |
131 | final class FileSystemWatcher
132 | {
133 | import std.typecons : Nullable;
134 |
135 | string globPattern;
136 | Nullable!ubyte kind;
137 |
138 | @safe this(string globPattern = string.init, Nullable!ubyte kind = Nullable!ubyte.init) pure nothrow
139 | {
140 | this.globPattern = globPattern;
141 | this.kind = kind;
142 | }
143 | }
144 |
145 | enum WatchKind : ubyte
146 | {
147 | create = 1,
148 | change = 2,
149 | delete_ = 4
150 | }
151 |
152 | final class WorkspaceSymbolParams
153 | {
154 | string query;
155 |
156 | @safe this() pure nothrow
157 | {
158 | }
159 | }
160 |
161 | final class ExecuteCommandParams
162 | {
163 | import std.json : JSONValue;
164 | import std.typecons : Nullable;
165 |
166 | string command;
167 | Nullable!(JSONValue[]) arguments;
168 |
169 | @safe this() pure nothrow
170 | {
171 | }
172 | }
173 |
174 | final class ExecuteCommandRegistrationOptions : RegistrationOptions
175 | {
176 | string[] commands;
177 |
178 | @safe this(string[] commands = string[].init) pure nothrow
179 | {
180 | this.commands = commands;
181 | }
182 | }
183 |
184 | final class ApplyWorkspaceEditParams
185 | {
186 | import dls.protocol.definitions : WorkspaceEdit;
187 |
188 | WorkspaceEdit edit;
189 |
190 | @safe this(WorkspaceEdit edit = WorkspaceEdit.init) pure nothrow
191 | {
192 | this.edit = edit;
193 | }
194 | }
195 |
196 | final class ApplyWorkspaceEditResponse
197 | {
198 | bool applied;
199 |
200 | @safe this() pure nothrow
201 | {
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/resources/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
26 |
--------------------------------------------------------------------------------
/source/dls/info.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.info;
22 |
23 | immutable currentVersion = import("dls-version.txt");
24 | immutable buildPlatform = import("build-platform.txt");
25 | immutable buildArch = import("build-arch.txt");
26 | immutable buildType = import("build-type.txt");
27 | immutable compilerVersion = import("compiler-version.txt");
28 |
--------------------------------------------------------------------------------
/source/dls/protocol/handlers.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.handlers;
22 |
23 | import std.json : JSONValue;
24 | import std.traits : isSomeFunction;
25 | import std.typecons : Nullable;
26 |
27 | alias RequestHandler = Nullable!JSONValue delegate(Nullable!JSONValue);
28 | alias NotificationHandler = void delegate(Nullable!JSONValue);
29 | alias ResponseHandler = void delegate(string id, Nullable!JSONValue);
30 |
31 | private shared RequestHandler[string] requestHandlers;
32 | private shared NotificationHandler[string] notificationHandlers;
33 | private shared ResponseHandler[string] responseHandlers;
34 | private shared ResponseHandler[string] runtimeResponseHandlers;
35 |
36 | class HandlerNotFoundException : Exception
37 | {
38 | this(const string method)
39 | {
40 | super("No handler found for method " ~ method);
41 | }
42 | }
43 |
44 | /++
45 | Checks if a method has a response handler registered for it. Used to determine
46 | if the server should send a request or a notification to the client (if the
47 | method has a response handler, then the server will expect a response and thus
48 | send a request instead of a notification).
49 | +/
50 | bool hasResponseHandler(const string method)
51 | {
52 | return (method in responseHandlers) !is null;
53 | }
54 |
55 | /++
56 | Registers a new handler of any kind (`RequestHandler`, `NotificationHandler` or
57 | `ResponseHandler`).
58 | +/
59 | void pushHandler(F)(const string method, F func)
60 | if (isSomeFunction!F && !is(F == RequestHandler)
61 | && !is(F == NotificationHandler) && !is(F == ResponseHandler))
62 | {
63 | import dls.util.json : convertFromJSON;
64 | import std.traits : Parameters, ReturnType;
65 |
66 | static if ((Parameters!F).length == 1)
67 | {
68 | pushHandler(method, (Nullable!JSONValue params) {
69 | import dls.util.json : convertToJSON;
70 |
71 | auto arg = convertFromJSON!((Parameters!F)[0])(params.isNull ? JSONValue(null) : params);
72 |
73 | static if (is(ReturnType!F == void))
74 | {
75 | func(arg);
76 | }
77 | else
78 | {
79 | return convertToJSON(func(arg));
80 | }
81 | });
82 | }
83 | else static if ((Parameters!F).length == 2)
84 | {
85 | pushHandler(method, (string id, Nullable!JSONValue params) => func(id,
86 | convertFromJSON!((Parameters!F)[1])(params.isNull ? JSONValue(null) : params)));
87 | }
88 | else
89 | {
90 | static assert(false);
91 | }
92 | }
93 |
94 | /++ Registers a new static `RequestHandler`. +/
95 | private void pushHandler(const string method, RequestHandler h)
96 | {
97 | requestHandlers[method] = h;
98 | }
99 |
100 | /++ Registers a new static `NotificationHandler`. +/
101 | private void pushHandler(const string method, NotificationHandler h)
102 | {
103 | notificationHandlers[method] = h;
104 | }
105 |
106 | /++ Registers a new static `ResponseHandler`. +/
107 | private void pushHandler(const string method, ResponseHandler h)
108 | {
109 | responseHandlers[method] = h;
110 | }
111 |
112 | /++ Registers a new dynamic `ResponseHandler` (used at runtime) +/
113 | void pushHandler(const string id, const string method)
114 | {
115 | runtimeResponseHandlers[id] = responseHandlers[method];
116 | }
117 |
118 | /++
119 | Returns the `RequestHandler`/`NotificationHandler`/`ResponseHandler`
120 | corresponding to a specific LSP method.
121 | +/
122 | T handler(T)(const string methodOrId)
123 | if (is(T == RequestHandler) || is(T == NotificationHandler) || is(T == ResponseHandler))
124 | {
125 | static if (is(T == RequestHandler))
126 | {
127 | alias handlers = requestHandlers;
128 | }
129 | else static if (is(T == NotificationHandler))
130 | {
131 | alias handlers = notificationHandlers;
132 | }
133 | else
134 | {
135 | alias handlers = runtimeResponseHandlers;
136 | }
137 |
138 | if (methodOrId in handlers)
139 | {
140 | auto h = handlers[methodOrId];
141 |
142 | static if (is(T == ResponseHandler))
143 | {
144 | runtimeResponseHandlers.remove(methodOrId);
145 | }
146 |
147 | return h;
148 | }
149 |
150 | throw new HandlerNotFoundException(methodOrId);
151 | }
152 |
--------------------------------------------------------------------------------
/source/dls/protocol/interfaces/dls.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.interfaces.dls;
22 |
23 | class TranslationParams
24 | {
25 | import dls.util.i18n : Tr;
26 |
27 | string tr;
28 |
29 | this(Tr tr = Tr._, string[] args = string[].init)
30 | {
31 | static import dls.util.i18n;
32 |
33 | this.tr = dls.util.i18n.tr(tr, args);
34 | }
35 | }
36 |
37 | class DlsUpgradeSizeParams : TranslationParams
38 | {
39 | import dls.util.i18n : Tr;
40 |
41 | size_t size;
42 |
43 | this(Tr tr = Tr._, string[] args = string[].init, size_t size = size_t.init)
44 | {
45 | super(tr, args);
46 | this.size = size;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/source/dls/protocol/interfaces/other.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.interfaces.other;
22 |
23 | class SetTraceParams
24 | {
25 | import dls.protocol.interfaces : InitializeParams;
26 |
27 | InitializeParams.Trace value;
28 | }
29 |
--------------------------------------------------------------------------------
/source/dls/protocol/jsonrpc.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.jsonrpc;
22 |
23 | import dls.util.i18n : Tr;
24 | import std.json : JSONValue;
25 | import std.typecons : Nullable, Tuple, tuple;
26 |
27 | private enum jsonrpcVersion = "2.0";
28 | private enum jsonrpcSeparator = "\r\n\r\n";
29 |
30 | abstract class Message
31 | {
32 | string jsonrpc = jsonrpcVersion;
33 | }
34 |
35 | class RequestMessage : Message
36 | {
37 | JSONValue id;
38 | string method;
39 | Nullable!JSONValue params;
40 | }
41 |
42 | class ResponseMessage : Message
43 | {
44 | JSONValue id;
45 | Nullable!JSONValue result;
46 | Nullable!ResponseError error;
47 | }
48 |
49 | class ResponseError
50 | {
51 | long code;
52 | string message;
53 | Nullable!JSONValue data;
54 |
55 | static ResponseError fromErrorCode(ErrorCodes errorCode, JSONValue data)
56 | {
57 | import dls.util.i18n : tr;
58 | import std.typecons : nullable;
59 |
60 | auto response = new ResponseError();
61 | response.code = errorCode[0];
62 | response.message = tr(errorCode[1]);
63 | response.data = data;
64 | return response.nullable;
65 | }
66 | }
67 |
68 | class NotificationMessage : Message
69 | {
70 | string method;
71 | Nullable!JSONValue params;
72 | }
73 |
74 | enum ErrorCodes : Tuple!(long, Tr)
75 | {
76 | //dfmt off
77 | parseError = tuple(-32_700L, Tr.app_rpc_errorCodes_parseError),
78 | invalidRequest = tuple(-32_600L, Tr.app_rpc_errorCodes_invalidRequest),
79 | methodNotFound = tuple(-32_601L, Tr.app_rpc_errorCodes_methodNotFound),
80 | invalidParams = tuple(-32_602L, Tr.app_rpc_errorCodes_invalidParams),
81 | internalError = tuple(-32_603L, Tr.app_rpc_errorCodes_internalError),
82 | serverErrorStart = tuple(-32_099L, Tr._),
83 | serverErrorEnd = tuple(-32_000L, Tr._),
84 | serverNotInitialized = tuple(-32_002L, Tr.app_rpc_errorCodes_serverNotInitialized),
85 | unknownErrorCode = tuple(-32_001L, Tr.app_rpc_errorCodes_unknownErrorCode),
86 | requestCancelled = tuple(-32_800L, Tr.app_rpc_errorCodes_requestCancelled),
87 | //dfmt on
88 | }
89 |
90 | class CancelParams
91 | {
92 | JSONValue id;
93 | }
94 |
95 | void sendError(ErrorCodes error, RequestMessage request, JSONValue data)
96 | {
97 | import std.typecons : nullable;
98 |
99 | if (request !is null)
100 | {
101 | send(request.id, Nullable!JSONValue(), ResponseError.fromErrorCode(error, data).nullable);
102 | }
103 | }
104 |
105 | /// Sends a request or a notification message.
106 | string send(string method, Nullable!JSONValue params = Nullable!JSONValue())
107 | {
108 | import dls.protocol.handlers : hasResponseHandler, pushHandler;
109 | import dls.protocol.logger : logger;
110 | import std.uuid : randomUUID;
111 |
112 | if (hasResponseHandler(method))
113 | {
114 | immutable id = randomUUID().toString();
115 | pushHandler(id, method);
116 | logger.log(`Sending request "%s": %s`, id, method);
117 | send!RequestMessage(JSONValue(id), method, params, Nullable!ResponseError());
118 | return id;
119 | }
120 |
121 | logger.log("Sending notification: %s", method);
122 | send!NotificationMessage(JSONValue(), method, params, Nullable!ResponseError());
123 | return null;
124 | }
125 |
126 | /// Sends a request or a notification message.
127 | string send(T)(string method, T params) if (!is(T : Nullable!JSONValue))
128 | {
129 | import dls.util.json : convertToJSON;
130 | import std.typecons : nullable;
131 |
132 | return send(method, convertToJSON(params));
133 | }
134 |
135 | /// Sends a response message.
136 | void send(JSONValue id, Nullable!JSONValue result,
137 | Nullable!ResponseError error = Nullable!ResponseError())
138 | {
139 | import dls.protocol.logger : logger;
140 |
141 | logger.log("Sending response with %s for request %s", error.isNull ? "result" : "error", id);
142 | send!ResponseMessage(id, null, result, error);
143 | }
144 |
145 | /// Sends a response message.
146 | private void send(T : Message)(JSONValue id, string method,
147 | Nullable!JSONValue payload, Nullable!ResponseError error)
148 | {
149 | import std.meta : AliasSeq;
150 | import std.traits : select;
151 |
152 | auto message = new T();
153 |
154 | __traits(getMember, message, select!(__traits(hasMember, T, "params"))("params", "result")) = payload;
155 |
156 | foreach (member; AliasSeq!("id", "method", "error"))
157 | {
158 | static if (__traits(hasMember, T, member))
159 | {
160 | mixin("message." ~ member ~ " = " ~ member ~ ";");
161 | }
162 | }
163 |
164 | send(message);
165 | }
166 |
167 | private void send(T : Message)(T m)
168 | {
169 | import dls.util.communicator : communicator;
170 | import dls.util.json : convertToJSON;
171 | import std.conv : text;
172 |
173 | auto message = convertToJSON(m);
174 | auto messageString = message.get().toString();
175 |
176 | synchronized
177 | {
178 | communicator.write(
179 | "Content-Length: "
180 | ~ text(messageString.length)
181 | ~ jsonrpcSeparator
182 | ~ messageString
183 | );
184 |
185 | communicator.flush();
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/source/dls/protocol/logger.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.logger;
22 |
23 | import dls.protocol.interfaces : InitializeParams, MessageType;
24 |
25 | private shared _logger = new shared LspLogger();
26 | private immutable int[InitializeParams.Trace] traceToType;
27 | private immutable string[MessageType] messageSeverity;
28 | private immutable logMessageFormat = "[%.24s] [%s] %s";
29 |
30 | shared static this()
31 | {
32 | import std.experimental.logger : LogLevel, globalLogLevel;
33 |
34 | globalLogLevel = LogLevel.off;
35 |
36 | //dfmt off
37 | traceToType = [
38 | InitializeParams.Trace.off : 0,
39 | InitializeParams.Trace.messages : MessageType.info,
40 | InitializeParams.Trace.verbose : MessageType.log
41 | ];
42 |
43 | messageSeverity = [
44 | MessageType.log : "D",
45 | MessageType.info : "I",
46 | MessageType.warning : "W",
47 | MessageType.error : "E"
48 | ];
49 | //dfmt on
50 | }
51 |
52 | @property shared(LspLogger) logger()
53 | {
54 | return _logger;
55 | }
56 |
57 | private shared class LspLogger
58 | {
59 | import dls.protocol.interfaces : MessageType;
60 | import std.format : format;
61 |
62 | private int _messageType;
63 |
64 | @property void trace(const InitializeParams.Trace t)
65 | {
66 | _messageType = traceToType[t];
67 | }
68 |
69 | void log(Args...)(const string message, const Args args) const
70 | {
71 | sendMessage(format(message, args), MessageType.log);
72 | }
73 |
74 | void info(Args...)(const string message, const Args args) const
75 | {
76 | sendMessage(format(message, args), MessageType.info);
77 | }
78 |
79 | void warning(Args...)(const string message, const Args args) const
80 | {
81 | sendMessage(format(message, args), MessageType.warning);
82 | }
83 |
84 | void error(Args...)(const string message, const Args args) const
85 | {
86 | sendMessage(format(message, args), MessageType.error);
87 | }
88 |
89 | private void sendMessage(const string message, const MessageType type) const
90 | {
91 | import dls.protocol.interfaces : LogMessageParams;
92 | import dls.protocol.jsonrpc : send;
93 | import dls.protocol.messages.methods : Window;
94 | import dls.protocol.state : initOptions;
95 | import std.datetime : Clock;
96 | import std.file : mkdirRecurse;
97 | import std.format : format;
98 | import std.path : dirName;
99 | import std.stdio : File;
100 |
101 | if (type <= _messageType)
102 | {
103 | if (initOptions.logFile.length > 0)
104 | {
105 | static bool firstLog = true;
106 |
107 | if (firstLog)
108 | {
109 | mkdirRecurse(dirName(initOptions.logFile));
110 | }
111 |
112 | synchronized
113 | {
114 | auto log = File(initOptions.logFile, firstLog ? "w" : "a");
115 | log.writefln(logMessageFormat, Clock.currTime.toString(),
116 | messageSeverity[type], message);
117 | log.flush();
118 | }
119 |
120 | if (firstLog)
121 | {
122 | firstLog = false;
123 | }
124 | }
125 |
126 | if (type != MessageType.log)
127 | {
128 | send(Window.logMessage, new LogMessageParams(type, format(logMessageFormat,
129 | Clock.currTime.toString(), messageSeverity[type], message)));
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/source/dls/protocol/messages/client.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.messages.client;
22 |
23 | import std.json : JSONValue;
24 |
25 | void registerCapability(string id, JSONValue nothing)
26 | {
27 | }
28 |
29 | void unregisterCapability(string id, JSONValue nothing)
30 | {
31 | }
32 |
--------------------------------------------------------------------------------
/source/dls/protocol/messages/methods.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.messages.methods;
22 |
23 | //dfmt off
24 | enum General : string
25 | {
26 | initialize = "initialize",
27 | initialized = "initialized",
28 | shutdown = "shutdown",
29 | exit = "exit",
30 | cancelRequest = "$/cancelRequest",
31 | }
32 |
33 | enum Window : string
34 | {
35 | showMessage = "window/showMessage",
36 | showMessageRequest = "window/showMessageRequest",
37 | logMessage = "window/logMessage",
38 | }
39 |
40 | enum Telemetry : string
41 | {
42 | event = "telemetry/event",
43 | }
44 |
45 | enum Client : string
46 | {
47 | registerCapability = "client/registerCapability",
48 | unregisterCapability = "client/unregisterCapability",
49 | }
50 |
51 | enum Workspace : string
52 | {
53 | workspaceFolders = "workspace/workspaceFolders",
54 | didChangeWorkspaceFolders = "workspace/didChangeWorkspaceFolders",
55 | configuration = "workspace/configuration",
56 | didChangeWatchedFiles = "workspace/didChangeWatchedFiles",
57 | symbol = "workspace/symbol",
58 | executeCommand = "workspace/executeCommand",
59 | applyEdit = "workspace/applyEdit",
60 | }
61 |
62 | enum TextDocument : string
63 | {
64 | didOpen = "textDocument/didOpen",
65 | didChange = "textDocument/didChange",
66 | willSave = "textDocument/willSave",
67 | willSaveWaitUntil = "textDocument/willSaveWaitUntil",
68 | didSave = "textDocument/didSave",
69 | didClose = "textDocument/didClose",
70 | publishDiagnostics = "textDocument/publishDiagnostics",
71 | completion = "textDocument/completion",
72 | completionResolve = "completionItem/resolve",
73 | hover = "textDocument/hover",
74 | signatureHelp = "textDocument/signatureHelp",
75 | declaration = "textDocument/declaration",
76 | definition = "textDocument/definition",
77 | typeDefinition = "textDocument/typeDefinition",
78 | implementation = "textDocument/implementation",
79 | references = "textDocument/references",
80 | documentHighlight = "textDocument/documentHighlight",
81 | documentSymbol = "textDocument/documentSymbol",
82 | codeAction = "textDocument/codeAction",
83 | codeLens = "textDocument/codeLens",
84 | codeLensResolve = "codeLens/resolve",
85 | documentLink = "textDocument/documentLink",
86 | documentLinkResolve = "documentLink/resolve",
87 | documentColor = "textDocument/documentColor",
88 | colorPresentation = "textDocument/colorPresentation",
89 | formatting = "textDocument/formatting",
90 | rangeFormatting = "textDocument/rangeFormatting",
91 | onTypeFormatting = "textDocument/onTypeFormatting",
92 | rename = "textDocument/rename",
93 | prepareRename = "textDocument/prepareRename",
94 | foldingRange = "textDocument/foldingRange",
95 | }
96 |
97 | interface Dls
98 | {
99 | enum UpgradeDls : string
100 | {
101 | didStart = "$/dls/upgradeDls/didStart",
102 | didStop = "$/dls/upgradeDls/didStop",
103 | didChangeTotalSize = "$/dls/upgradeDls/didChangeTotalSize",
104 | didChangeCurrentSize = "$/dls/upgradeDls/didChangeCurrentSize",
105 | didExtract = "$/dls/upgradeDls/didExtract",
106 | }
107 |
108 | enum UpgradeSelections : string
109 | {
110 | didStart = "$/dls/upgradeSelections/didStart",
111 | didStop = "$/dls/upgradeSelections/didStop",
112 | }
113 | }
114 | //dfmt on
115 |
--------------------------------------------------------------------------------
/source/dls/protocol/messages/other.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.messages.other;
22 |
23 | import dls.protocol.interfaces.other;
24 |
25 | @("$")
26 | void setTraceNotification(SetTraceParams params)
27 | {
28 | import dls.protocol.logger : logger;
29 |
30 | logger.info("Setting trace level to: %s", params.value);
31 | logger.trace = params.value;
32 | }
33 |
--------------------------------------------------------------------------------
/source/dls/protocol/messages/window.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.messages.window;
22 |
23 | import dls.protocol.interfaces : MessageActionItem;
24 | import std.typecons : Nullable;
25 |
26 | void showMessageRequest(string id, Nullable!MessageActionItem item)
27 | {
28 | import dls.protocol.logger : logger;
29 | import dls.tools.symbol_tool : SymbolTool;
30 | import dls.util.i18n : Tr, tr;
31 | import dls.util.uri : Uri;
32 | import std.concurrency : locate, receiveOnly, send;
33 | import std.process : browse;
34 |
35 | while (id !in Util.messageRequestInfo)
36 | {
37 | auto data = receiveOnly!(Util.ThreadMessageData)();
38 | Util.bindMessageToRequestId(data[0], data[1], data[2]);
39 | }
40 |
41 | if (!item.isNull)
42 | {
43 | switch (Util.messageRequestInfo[id][0])
44 | {
45 | case Tr.app_upgradeSelections:
46 | if (item.title == tr(Tr.app_upgradeSelections_upgrade))
47 | {
48 | auto uri = new Uri(Util.messageRequestInfo[id][1]);
49 | SymbolTool.instance.upgradeSelections(uri);
50 | }
51 |
52 | break;
53 |
54 | case Tr.app_upgradeDls:
55 | send(locate(Util.messageRequestInfo[id][1]),
56 | item.title == tr(Tr.app_upgradeDls_upgrade));
57 | break;
58 |
59 | case Tr.app_showChangelog:
60 | if (item.title == tr(Tr.app_showChangelog_show))
61 | {
62 | logger.info("Opening changelog in browser");
63 | browse(Util.messageRequestInfo[id][1]);
64 | }
65 |
66 | break;
67 |
68 | default:
69 | assert(false, Util.messageRequestInfo[id][0] ~ " cannot be handled as requests");
70 | }
71 | }
72 |
73 | Util.messageRequestInfo.remove(id);
74 | }
75 |
76 | final abstract class Util
77 | {
78 | import dls.protocol.jsonrpc : send;
79 | import dls.protocol.messages.methods : Window;
80 | import dls.util.i18n : Tr, tr, trType;
81 | import std.array : array;
82 | import std.algorithm : map;
83 | import std.typecons : Tuple, tuple;
84 |
85 | shared alias ThreadMessageData = Tuple!(string, Tr, string);
86 |
87 | private static Tuple!(Tr, string)[string] messageRequestInfo;
88 |
89 | static void sendMessage(Tr message, string[] args = [])
90 | {
91 | import dls.protocol.interfaces : ShowMessageParams;
92 |
93 | send(Window.showMessage, new ShowMessageParams(trType(message), tr(message, args)));
94 | }
95 |
96 | static string sendMessageRequest(Tr message, Tr[] actions, string[] args = [])
97 | {
98 | import dls.protocol.interfaces : ShowMessageRequestParams;
99 | import std.typecons : nullable;
100 |
101 | return send(Window.showMessageRequest, new ShowMessageRequestParams(trType(message),
102 | tr(message, args), actions.map!(a => new MessageActionItem(tr(a,
103 | args))).array.nullable));
104 | }
105 |
106 | static void bindMessageToRequestId(string id, Tr message, string data = null)
107 | {
108 | messageRequestInfo[id] = tuple(message, data);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/source/dls/protocol/messages/workspace.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.messages.workspace;
22 |
23 | import dls.protocol.interfaces : SymbolInformation;
24 | import dls.protocol.interfaces.workspace;
25 | import std.json : JSONValue;
26 | import std.typecons : Nullable;
27 |
28 | void workspaceFolders(string id, Nullable!(WorkspaceFolder[]) folders)
29 | {
30 | import dls.protocol.jsonrpc : send;
31 | import dls.protocol.messages.methods : Workspace;
32 | import dls.protocol.state : initState;
33 | import dls.tools.analysis_tool : AnalysisTool;
34 | import dls.tools.symbol_tool : SymbolTool;
35 | import dls.tools.tool : Tool;
36 | import dls.util.uri : Uri;
37 | import std.typecons : nullable;
38 |
39 | if (!folders.isNull)
40 | {
41 | ConfigurationItem[] items;
42 |
43 | foreach (workspaceFolder; folders)
44 | {
45 | auto uri = new Uri(workspaceFolder.uri);
46 | items ~= new ConfigurationItem(uri.toString().nullable);
47 | Tool.instance.updateConfig(uri, JSONValue());
48 | SymbolTool.instance.importPath(uri);
49 | AnalysisTool.instance.addAnalysisConfig(uri);
50 | }
51 |
52 | immutable conf = !initState.capabilities.workspace.isNull
53 | && !initState.capabilities.workspace.configuration.isNull
54 | && initState.capabilities.workspace.configuration;
55 |
56 | if (conf)
57 | {
58 | send(Workspace.configuration, new ConfigurationParams(items));
59 | }
60 | }
61 | }
62 |
63 | void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params)
64 | {
65 | import dls.tools.analysis_tool : AnalysisTool;
66 | import dls.tools.symbol_tool : SymbolTool;
67 | import dls.tools.tool : Tool;
68 | import dls.util.uri : Uri;
69 | import std.typecons : nullable;
70 |
71 | workspaceFolders(null, params.event.added.nullable);
72 |
73 | foreach (folder; params.event.removed)
74 | {
75 | auto uri = new Uri(folder.uri);
76 | Tool.instance.removeConfig(uri);
77 | SymbolTool.instance.clearPath(uri);
78 | AnalysisTool.instance.removeAnalysisConfig(uri);
79 | }
80 | }
81 |
82 | void configuration(string id, JSONValue[] configs)
83 | {
84 | import dls.protocol.logger : logger;
85 | import dls.tools.tool : Tool;
86 |
87 | auto uris = null ~ Tool.instance.workspacesUris;
88 |
89 | logger.info("Updating workspace configurations");
90 |
91 | for (size_t i; i < configs.length && i < uris.length; ++i)
92 | {
93 | auto config = configs[i];
94 |
95 | if ("d" in config && "dls" in config["d"])
96 | {
97 | Tool.instance.updateConfig(uris[i], config["d"]["dls"]);
98 | }
99 | }
100 | }
101 |
102 | void didChangeConfiguration(DidChangeConfigurationParams params)
103 | {
104 | import dls.protocol.jsonrpc : send;
105 | import dls.protocol.logger : logger;
106 | import dls.protocol.messages.methods : Workspace;
107 | import dls.protocol.state : initState;
108 | import dls.tools.configuration : Configuration;
109 | import dls.tools.tool : Tool;
110 | import std.typecons : Nullable, nullable;
111 |
112 | logger.info("Configuration changed");
113 |
114 | immutable conf = !initState.capabilities.workspace.isNull
115 | && !initState.capabilities.workspace.configuration.isNull
116 | && initState.capabilities.workspace.configuration;
117 |
118 | if (conf)
119 | {
120 | auto items = [new ConfigurationItem(Nullable!string(null))];
121 |
122 | foreach (uri; Tool.instance.workspacesUris)
123 | {
124 | items ~= new ConfigurationItem(uri.toString().nullable);
125 | }
126 |
127 | send(Workspace.configuration, new ConfigurationParams(items));
128 | }
129 | else if ("d" in params.settings && "dls" in params.settings["d"])
130 | {
131 | logger.info("Updating configuration");
132 | Tool.instance.updateConfig(null, params.settings["d"]["dls"]);
133 | }
134 | }
135 |
136 | void didChangeWatchedFiles(DidChangeWatchedFilesParams params)
137 | {
138 | import dls.protocol.interfaces : FileChangeType, PublishDiagnosticsParams;
139 | import dls.protocol.jsonrpc : send;
140 | import dls.protocol.logger : logger;
141 | import dls.protocol.messages.methods : TextDocument;
142 | import dls.tools.analysis_tool : AnalysisTool;
143 | import dls.tools.symbol_tool : SymbolTool;
144 | import dls.util.document : Document;
145 | import dls.util.uri : Uri, sameFile;
146 | import std.algorithm : canFind, filter, startsWith;
147 | import std.file : exists, isFile;
148 | import std.path : baseName, dirName, extension, pathSplitter;
149 |
150 | foreach (event; params.changes.filter!(event => event.type == FileChangeType.deleted))
151 | {
152 | auto workspaceUri = SymbolTool.instance.getWorkspace(new Uri(event.uri));
153 |
154 | if (workspaceUri !is null && !exists(workspaceUri.path))
155 | {
156 | SymbolTool.instance.clearPath(workspaceUri);
157 | }
158 | }
159 |
160 | outer: foreach (event; params.changes)
161 | {
162 | auto uri = new Uri(event.uri);
163 | auto dirPath = dirName(uri.path);
164 |
165 | foreach (part; pathSplitter(dirPath))
166 | {
167 | if (part.startsWith('.'))
168 | {
169 | continue outer;
170 | }
171 | }
172 |
173 | auto dirUri = Uri.fromPath(dirPath);
174 |
175 | logger.info("Resource %s: %s", event.type, uri.path);
176 |
177 | if (exists(uri.path) && !isFile(uri.path))
178 | {
179 | continue;
180 | }
181 |
182 | switch (baseName(uri.path))
183 | {
184 | case "dub.json", "dub.sdl":
185 | if (event.type != FileChangeType.deleted)
186 | {
187 | SymbolTool.instance.importDubProject(dirUri);
188 | }
189 |
190 | continue;
191 |
192 | case "dub.selections.json":
193 | if (event.type != FileChangeType.deleted)
194 | {
195 | SymbolTool.instance.importDubSelections(dirUri);
196 | }
197 |
198 | continue;
199 |
200 | case ".gitmodules":
201 | if (event.type != FileChangeType.deleted)
202 | {
203 | SymbolTool.instance.importGitSubmodules(dirUri);
204 | }
205 |
206 | continue;
207 |
208 | default:
209 | break;
210 | }
211 |
212 | switch (extension(uri.path))
213 | {
214 | case ".d", ".di":
215 | Uri[] discarded;
216 |
217 | if (event.type == FileChangeType.deleted)
218 | {
219 | send(TextDocument.publishDiagnostics, new PublishDiagnosticsParams(uri, []));
220 | }
221 | else if (!Document.uris.canFind!sameFile(uri)
222 | && AnalysisTool.instance.getScannableFilesUris(discarded)
223 | .canFind!sameFile(uri))
224 | {
225 | send(TextDocument.publishDiagnostics, new PublishDiagnosticsParams(uri,
226 | AnalysisTool.instance.diagnostics(uri)));
227 | }
228 |
229 | continue;
230 |
231 | case ".ini":
232 | AnalysisTool.instance.updateAnalysisConfig(dirUri);
233 | continue;
234 |
235 | default:
236 | break;
237 | }
238 | }
239 | }
240 |
241 | SymbolInformation[] symbol(WorkspaceSymbolParams params)
242 | {
243 | import dls.tools.symbol_tool : SymbolTool;
244 |
245 | return SymbolTool.instance.symbol(params.query);
246 | }
247 |
248 | JSONValue executeCommand(ExecuteCommandParams params)
249 | {
250 | import dls.tools.command_tool : CommandTool;
251 |
252 | return CommandTool.instance.executeCommand(params.command,
253 | params.arguments.isNull ? [] : params.arguments.get());
254 | }
255 |
256 | void applyEdit(string id, ApplyWorkspaceEditResponse response)
257 | {
258 | }
259 |
--------------------------------------------------------------------------------
/source/dls/protocol/state.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.protocol.state;
22 |
23 | import dls.protocol.interfaces : InitializeParams;
24 |
25 | private InitializeParams _initState;
26 | private InitializeParams.InitializationOptions _commandLineOptions;
27 |
28 | static this()
29 | {
30 | _initState = new InitializeParams();
31 | _commandLineOptions = new InitializeParams.InitializationOptions();
32 | }
33 |
34 | @property InitializeParams initState()
35 | {
36 | return _initState;
37 | }
38 |
39 | @property void initState(InitializeParams params)
40 | {
41 | import dls.protocol.logger : logger;
42 | import dls.util.disposable_fiber : DisposableFiber;
43 |
44 | assert(params !is null);
45 | _initState = params;
46 |
47 | if (!params.trace.isNull)
48 | {
49 | logger.trace = params.trace;
50 | }
51 |
52 | if (_initState.initializationOptions.isNull)
53 | {
54 | _initState.initializationOptions = _commandLineOptions;
55 | }
56 | else
57 | {
58 | merge(params.initializationOptions.get(), _commandLineOptions);
59 | }
60 |
61 | DisposableFiber.safeMode = initOptions.safeMode;
62 | }
63 |
64 | @property InitializeParams.InitializationOptions initOptions()
65 | {
66 | return initState.initializationOptions.isNull ? _commandLineOptions
67 | : _initState.initializationOptions;
68 | }
69 |
70 | @property void initOptions(InitializeParams.InitializationOptions options)
71 | {
72 | assert(options !is null);
73 | _commandLineOptions = options;
74 | }
75 |
76 | private void merge(T)(ref T options, const T addins)
77 | {
78 | import std.meta : Alias;
79 | import std.traits : isSomeFunction, isType;
80 | import dls.protocol.logger : logger;
81 |
82 | static if (is(T == class))
83 | {
84 | immutable reference = new T();
85 | }
86 | else
87 | {
88 | immutable reference = T.init;
89 | }
90 |
91 | foreach (member; __traits(allMembers, T))
92 | {
93 | alias m = Alias!(__traits(getMember, T, member));
94 | alias optionsMember = Alias!(mixin("options." ~ member));
95 | alias addinsMember = Alias!(mixin("addins." ~ member));
96 |
97 | static if (__traits(getProtection, m) != "public" || isType!m || isSomeFunction!m)
98 | {
99 | continue;
100 | }
101 | else static if (is(typeof(m) == class))
102 | {
103 | merge(mixin("options." ~ member), mixin("addins." ~ member));
104 | }
105 | else
106 | {
107 | if (mixin("options." ~ member) == mixin("reference." ~ member))
108 | {
109 | mixin("options." ~ member) = mixin("addins." ~ member);
110 | }
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/source/dls/tools/command_tool.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.tools.command_tool;
22 |
23 | import dls.tools.tool : Tool;
24 |
25 | enum Commands : string
26 | {
27 | workspaceEdit = "workspaceEdit",
28 | codeAction_analysis_disableCheck = "codeAction.analysis.disableCheck"
29 | }
30 |
31 | class CommandTool : Tool
32 | {
33 | import std.json : JSONValue;
34 |
35 | private static CommandTool _instance;
36 |
37 | static void initialize(CommandTool tool)
38 | {
39 | _instance = tool;
40 | }
41 |
42 | static void shutdown()
43 | {
44 | destroy(_instance);
45 | }
46 |
47 | @property static CommandTool instance()
48 | {
49 | return _instance;
50 | }
51 |
52 | @property string[] commands()
53 | {
54 | string[] result;
55 |
56 | foreach (member; __traits(allMembers, Commands))
57 | {
58 | result ~= mixin("Commands." ~ member);
59 | }
60 |
61 | return result;
62 | }
63 |
64 | JSONValue executeCommand(const string commandName, const JSONValue[] arguments)
65 | {
66 | import dls.protocol.definitions : WorkspaceEdit;
67 | import dls.protocol.errors : InvalidParamsException;
68 | import dls.protocol.interfaces : ApplyWorkspaceEditParams;
69 | import dls.protocol.jsonrpc : send;
70 | import dls.protocol.logger : logger;
71 | import dls.protocol.messages.methods : Workspace;
72 | import dls.tools.analysis_tool : AnalysisTool;
73 | import dls.util.json : convertFromJSON;
74 | import dls.util.uri : Uri;
75 | import std.json : JSONException;
76 | import std.format : format;
77 |
78 | logger.info("Executing command %s with arguments %s", commandName, arguments);
79 |
80 | try
81 | {
82 | final switch (convertFromJSON!Commands(JSONValue(commandName)))
83 | {
84 | case Commands.workspaceEdit:
85 | send(Workspace.applyEdit,
86 | new ApplyWorkspaceEditParams(convertFromJSON!WorkspaceEdit(arguments[0])));
87 | break;
88 |
89 | case Commands.codeAction_analysis_disableCheck:
90 | AnalysisTool.instance.disableCheck(new Uri(convertFromJSON!string(arguments[0])),
91 | convertFromJSON!string(arguments[1]));
92 | break;
93 | }
94 | }
95 | catch (JSONException e)
96 | {
97 | throw new InvalidParamsException(format!"unknown command: %s"(commandName));
98 | }
99 |
100 | return JSONValue(null);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/source/dls/tools/configuration.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.tools.configuration;
22 |
23 | class Configuration
24 | {
25 | import std.json : JSONValue;
26 |
27 | static class SymbolConfiguration
28 | {
29 | string[] importPaths;
30 | bool listLocalSymbols;
31 | }
32 |
33 | static class AnalysisConfiguration
34 | {
35 | string configFile = "dscanner.ini";
36 | string[] filePatterns = [];
37 | }
38 |
39 | static class FormatConfiguration
40 | {
41 | static enum Engine : string
42 | {
43 | dfmt = "dfmt",
44 | builtin = "builtin"
45 | }
46 |
47 | static enum BraceStyle : string
48 | {
49 | allman = "allman",
50 | otbs = "otbs",
51 | stroustrup = "stroustrup"
52 | }
53 |
54 | static enum EndOfLine : string
55 | {
56 | lf = "lf",
57 | cr = "cr",
58 | crlf = "crlf"
59 | }
60 |
61 | static enum TemplateConstraintsStyle : string
62 | {
63 | conditionalNewlineIndent = "conditionalNewlineIndent",
64 | conditionalNewline = "conditionalNewline",
65 | alwaysNewline = "alwaysNewline",
66 | alwaysNewlineIndent = "alwaysNewlineIndent"
67 | }
68 |
69 | Engine engine = Engine.dfmt;
70 | EndOfLine endOfLine = EndOfLine.lf;
71 | bool insertFinalNewline = true;
72 | bool trimTrailingWhitespace = true;
73 | int maxLineLength = 120;
74 | int softMaxLineLength = 80;
75 | BraceStyle braceStyle = BraceStyle.allman;
76 | bool spaceAfterCasts = true;
77 | bool spaceAfterKeywords = true;
78 | bool spaceBeforeAAColons = false;
79 | bool spaceBeforeFunctionParameters = false;
80 | bool spaceBeforeSelectiveImportColons = true;
81 | bool alignSwitchStatements = true;
82 | bool compactLabeledStatements = true;
83 | bool outdentAttributes = true;
84 | bool splitOperatorsAtLineEnd = false;
85 | TemplateConstraintsStyle templateConstraintsStyle = TemplateConstraintsStyle
86 | .conditionalNewlineIndent;
87 | bool templateConstraintsSingleIndent = false;
88 | }
89 |
90 | SymbolConfiguration symbol;
91 | AnalysisConfiguration analysis;
92 | FormatConfiguration format;
93 |
94 | this()
95 | {
96 | symbol = new SymbolConfiguration();
97 | analysis = new AnalysisConfiguration();
98 | format = new FormatConfiguration();
99 | }
100 |
101 | void merge(JSONValue json)
102 | {
103 | merge!(typeof(this))(json);
104 | }
105 |
106 | private void merge(T)(JSONValue json)
107 | {
108 | import dls.util.json : convertFromJSON;
109 | import std.json : JSONType;
110 | import std.meta : Alias;
111 | import std.traits : isSomeFunction, isType;
112 |
113 | if (json.type != JSONType.object)
114 | {
115 | return;
116 | }
117 |
118 | foreach (member; __traits(allMembers, T))
119 | {
120 | if (member !in json)
121 | {
122 | continue;
123 | }
124 |
125 | alias m = Alias!(__traits(getMember, T, member));
126 |
127 | static if (!isType!(m) && !isSomeFunction!(m))
128 | {
129 | static if (is(m == class))
130 | {
131 | merge(m, json[member]);
132 | }
133 | else
134 | {
135 | m = convertFromJSON!(typeof(m))(json[member]);
136 | }
137 | }
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/source/dls/tools/format_tool/internal/dfmt_format_tool.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.tools.format_tool.internal.dfmt_format_tool;
22 |
23 | import dls.tools.format_tool.internal.format_tool : FormatTool;
24 |
25 | private immutable configPattern = "dummy.d";
26 |
27 | class DfmtFormatTool : FormatTool
28 | {
29 | import dfmt.config : Config;
30 | import dls.protocol.definitions : Position, Range, TextEdit;
31 | import dls.protocol.interfaces : FormattingOptions;
32 | import dls.util.uri : Uri;
33 |
34 | override TextEdit[] formatting(const Uri uri, const FormattingOptions options)
35 | {
36 | import dfmt.formatter : format;
37 | import dls.protocol.logger : logger;
38 | import dls.util.document : Document;
39 | import std.outbuffer : OutBuffer;
40 |
41 | logger.info("Formatting %s", uri.path);
42 |
43 | const document = Document.get(uri);
44 | auto contents = cast(ubyte[]) document.toString();
45 | auto config = getFormatConfig(uri, options);
46 | auto buffer = new OutBuffer();
47 | format(uri.path, contents, buffer, &config);
48 | return diff(uri, buffer.toString());
49 | }
50 |
51 | private Config getFormatConfig(const Uri uri, const FormattingOptions options)
52 | {
53 | import dfmt.config : BraceStyle, TemplateConstraintStyle;
54 | import dfmt.editorconfig : EOL, IndentStyle, OptionalBoolean, getConfigFor;
55 | import dls.tools.configuration : Configuration;
56 | import dls.tools.symbol_tool : SymbolTool;
57 |
58 | static OptionalBoolean toOptBool(bool b)
59 | {
60 | return b ? OptionalBoolean.t : OptionalBoolean.f;
61 | }
62 |
63 | auto formatConf = getConfig(SymbolTool.instance.getWorkspace(uri)).format;
64 | Config config;
65 | config.initializeWithDefaults();
66 | config.pattern = configPattern;
67 | config.indent_style = options.insertSpaces ? IndentStyle.space : IndentStyle.tab;
68 | config.indent_size = cast(typeof(config.indent_size)) options.tabSize;
69 | config.tab_width = config.indent_size;
70 | config.max_line_length = formatConf.maxLineLength;
71 | config.dfmt_align_switch_statements = toOptBool(formatConf.alignSwitchStatements);
72 | config.dfmt_outdent_attributes = toOptBool(formatConf.outdentAttributes);
73 | config.dfmt_soft_max_line_length = formatConf.softMaxLineLength;
74 | config.dfmt_space_after_cast = toOptBool(formatConf.spaceAfterCasts);
75 | config.dfmt_space_after_keywords = toOptBool(formatConf.spaceAfterKeywords);
76 | config.dfmt_space_before_function_parameters = toOptBool(
77 | formatConf.spaceBeforeFunctionParameters);
78 | config.dfmt_split_operator_at_line_end = toOptBool(formatConf.splitOperatorsAtLineEnd);
79 | config.dfmt_selective_import_space = toOptBool(formatConf.spaceBeforeSelectiveImportColons);
80 | config.dfmt_compact_labeled_statements = formatConf.compactLabeledStatements
81 | ? OptionalBoolean.t : OptionalBoolean.f;
82 | config.dfmt_single_template_constraint_indent = toOptBool(
83 | formatConf.templateConstraintsSingleIndent);
84 |
85 | final switch (formatConf.endOfLine)
86 | {
87 | case Configuration.FormatConfiguration.EndOfLine.lf:
88 | config.end_of_line = EOL.lf;
89 | break;
90 | case Configuration.FormatConfiguration.EndOfLine.cr:
91 | config.end_of_line = EOL.cr;
92 | break;
93 | case Configuration.FormatConfiguration.EndOfLine.crlf:
94 | config.end_of_line = EOL.crlf;
95 | break;
96 | }
97 |
98 | final switch (formatConf.braceStyle)
99 | {
100 | case Configuration.FormatConfiguration.BraceStyle.allman:
101 | config.dfmt_brace_style = BraceStyle.allman;
102 | break;
103 | case Configuration.FormatConfiguration.BraceStyle.otbs:
104 | config.dfmt_brace_style = BraceStyle.otbs;
105 | break;
106 | case Configuration.FormatConfiguration.BraceStyle.stroustrup:
107 | config.dfmt_brace_style = BraceStyle.stroustrup;
108 | break;
109 | }
110 |
111 | final switch (formatConf.templateConstraintsStyle)
112 | {
113 | case Configuration.FormatConfiguration.TemplateConstraintsStyle.conditionalNewlineIndent:
114 | config.dfmt_template_constraint_style
115 | = TemplateConstraintStyle.conditional_newline_indent;
116 | break;
117 | case Configuration.FormatConfiguration.TemplateConstraintsStyle.conditionalNewline:
118 | config.dfmt_template_constraint_style = TemplateConstraintStyle.conditional_newline;
119 | break;
120 | case Configuration.FormatConfiguration.TemplateConstraintsStyle.alwaysNewline:
121 | config.dfmt_template_constraint_style = TemplateConstraintStyle.always_newline;
122 | break;
123 | case Configuration.FormatConfiguration.TemplateConstraintsStyle.alwaysNewlineIndent:
124 | config.dfmt_template_constraint_style = TemplateConstraintStyle.always_newline_indent;
125 | break;
126 | }
127 |
128 | auto fileConfig = getConfigFor!Config(uri.path);
129 | fileConfig.pattern = configPattern;
130 | config.merge(fileConfig, configPattern);
131 | return config;
132 | }
133 |
134 | private TextEdit[] diff(const Uri uri, const string after)
135 | {
136 | import dls.util.document : Document;
137 | import std.ascii : isWhite;
138 | import std.utf : decode;
139 |
140 | const document = Document.get(uri);
141 | immutable before = document.toString();
142 | size_t i;
143 | size_t j;
144 | TextEdit[] result;
145 |
146 | size_t startIndex;
147 | size_t stopIndex;
148 | string text;
149 |
150 | bool pushTextEdit()
151 | {
152 | if (startIndex != stopIndex || text.length > 0)
153 | {
154 | result ~= new TextEdit(new Range(document.positionAtByte(startIndex),
155 | document.positionAtByte(stopIndex)), text);
156 | return true;
157 | }
158 |
159 | return false;
160 | }
161 |
162 | while (i < before.length || j < after.length)
163 | {
164 | auto newI = i;
165 | auto newJ = j;
166 | dchar beforeChar;
167 | dchar afterChar;
168 |
169 | if (newI < before.length)
170 | {
171 | beforeChar = decode(before, newI);
172 | }
173 |
174 | if (newJ < after.length)
175 | {
176 | afterChar = decode(after, newJ);
177 | }
178 |
179 | if (i < before.length && j < after.length && beforeChar == afterChar)
180 | {
181 | i = newI;
182 | j = newJ;
183 |
184 | if (pushTextEdit())
185 | {
186 | startIndex = stopIndex;
187 | text = "";
188 | }
189 | }
190 |
191 | if (startIndex == stopIndex)
192 | {
193 | startIndex = i;
194 | stopIndex = i;
195 | }
196 |
197 | auto addition = !isWhite(beforeChar) && isWhite(afterChar);
198 | immutable deletion = isWhite(beforeChar) && !isWhite(afterChar);
199 |
200 | if (!addition && !deletion)
201 | {
202 | addition = before.length - i < after.length - j;
203 | }
204 |
205 | if (addition && j < after.length)
206 | {
207 | text ~= after[j .. newJ];
208 | j = newJ;
209 | }
210 | else if (i < before.length)
211 | {
212 | stopIndex = newI;
213 | i = newI;
214 | }
215 | }
216 |
217 | pushTextEdit();
218 | return result;
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/source/dls/tools/format_tool/internal/format_tool.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.tools.format_tool.internal.format_tool;
22 |
23 | import dls.protocol.definitions : Range;
24 | import dls.tools.tool : Tool;
25 |
26 | abstract class FormatTool : Tool
27 | {
28 | import dls.protocol.definitions : Position, TextEdit;
29 | import dls.protocol.interfaces : FormattingOptions;
30 | import dls.util.uri : Uri;
31 |
32 | private static FormatTool _instance;
33 |
34 | static void initialize(FormatTool tool)
35 | {
36 | import dls.tools.configuration : Configuration;
37 | import dls.tools.format_tool.internal.builtin_format_tool : BuiltinFormatTool;
38 | import dls.tools.format_tool.internal.dfmt_format_tool : DfmtFormatTool;
39 |
40 | _instance = tool;
41 | _instance.addConfigHook("engine", (const Uri uri) {
42 | auto config = getConfig(null);
43 |
44 | if (config.format.engine == Configuration.FormatConfiguration.Engine.dfmt
45 | && typeid(_instance) == typeid(DfmtFormatTool))
46 | {
47 | return;
48 | }
49 |
50 | FormatTool.shutdown();
51 | FormatTool.initialize(config.format.engine == Configuration.FormatConfiguration.Engine.dfmt
52 | ? new DfmtFormatTool() : new BuiltinFormatTool());
53 | });
54 | }
55 |
56 | static void shutdown()
57 | {
58 | _instance.removeConfigHooks();
59 | destroy(_instance);
60 | }
61 |
62 | @property static FormatTool instance()
63 | {
64 | return _instance;
65 | }
66 |
67 | TextEdit[] formatting(const Uri uri, const FormattingOptions options);
68 |
69 | TextEdit[] rangeFormatting(const Uri uri, const Range range, const FormattingOptions options)
70 | {
71 | import dls.util.document : Document;
72 | import std.algorithm : filter;
73 | import std.array : array;
74 |
75 | const document = Document.get(uri);
76 | document.validatePosition(range.start);
77 | return formatting(uri, options).filter!((edit) => edit.range.isValidEditFor(range)).array;
78 | }
79 |
80 | TextEdit[] onTypeFormatting(const Uri uri, const Position position,
81 | const FormattingOptions options)
82 | {
83 | import dls.util.document : Document;
84 | import std.algorithm : filter;
85 | import std.array : array;
86 | import std.string : stripRight;
87 |
88 | const document = Document.get(uri);
89 | document.validatePosition(position);
90 |
91 | if (position.character != stripRight(document.lines[position.line]).length)
92 | {
93 | return [];
94 | }
95 |
96 | return formatting(uri, options).filter!(edit => edit.range.start.line == position.line
97 | || edit.range.end.line == position.line).array;
98 | }
99 | }
100 |
101 | package bool isValidEditFor(const Range editRange, const Range formatRange)
102 | {
103 | //dfmt off
104 | return (editRange.start.line < formatRange.end.line
105 | || (editRange.start.line == formatRange.end.line && editRange.start.character <= formatRange.end.character))
106 | && (editRange.end.line > formatRange.start.line
107 | || (editRange.end.line == formatRange.start.line && editRange.end.character >= formatRange.start.character));
108 | //dfmt on
109 | }
110 |
--------------------------------------------------------------------------------
/source/dls/tools/format_tool/package.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.tools.format_tool;
22 |
23 | public import dls.tools.format_tool.internal.builtin_format_tool;
24 | public import dls.tools.format_tool.internal.dfmt_format_tool;
25 | public import dls.tools.format_tool.internal.format_tool;
26 |
--------------------------------------------------------------------------------
/source/dls/tools/symbol_tool/package.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.tools.symbol_tool;
22 |
23 | public import dls.tools.symbol_tool.internal.symbol_tool;
24 |
--------------------------------------------------------------------------------
/source/dls/tools/tool.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.tools.tool;
22 |
23 | import dls.util.uri : Uri;
24 |
25 | alias Hook = void delegate(const Uri uri);
26 |
27 | class Tool
28 | {
29 | import dls.tools.configuration : Configuration;
30 | import std.json : JSONValue;
31 |
32 | private static Tool _instance;
33 | private static Configuration _globalConfig;
34 | private static JSONValue[string] _workspacesConfigs;
35 | private static Hook[string] _configHooks;
36 |
37 | @property Uri[] workspacesUris()
38 | {
39 | import std.algorithm : map, sort;
40 | import std.array : array;
41 |
42 | return _workspacesConfigs.keys.sort().map!(u => Uri.fromPath(u)).array;
43 | }
44 |
45 | static void initialize(Tool tool)
46 | {
47 | _instance = tool;
48 | _globalConfig = new Configuration();
49 | }
50 |
51 | static void shutdown()
52 | {
53 | _globalConfig = new Configuration();
54 | _workspacesConfigs.clear();
55 | _configHooks.clear();
56 | }
57 |
58 | protected static Configuration getConfig(const Uri uri)
59 | {
60 | import dls.util.json : convertToJSON;
61 |
62 | if (uri is null || uri.path !in _workspacesConfigs)
63 | {
64 | return _globalConfig;
65 | }
66 |
67 | auto config = new Configuration();
68 | config.merge(convertToJSON(_globalConfig));
69 | config.merge(_workspacesConfigs[uri.path]);
70 | return config;
71 | }
72 |
73 | @property static Tool instance()
74 | {
75 | return _instance;
76 | }
77 |
78 | void updateConfig(const Uri uri, JSONValue json)
79 | {
80 | import dls.protocol.state : initState;
81 |
82 | if (uri is null || uri.path.length == 0)
83 | {
84 | _globalConfig.merge(json);
85 | }
86 | else
87 | {
88 | _workspacesConfigs[uri.path] = json;
89 | }
90 |
91 | foreach (hook; _configHooks.byValue)
92 | {
93 | hook(uri is null ? initState.rootUri.isNull ? null : new Uri(initState.rootUri) : uri);
94 | }
95 | }
96 |
97 | void removeConfig(const Uri uri)
98 | {
99 | if (uri in _workspacesConfigs)
100 | {
101 | _workspacesConfigs.remove(uri.path);
102 | }
103 | }
104 |
105 | protected void addConfigHook(string name, Hook hook)
106 | {
107 | _configHooks[this.toString() ~ '/' ~ name] = hook;
108 | }
109 |
110 | protected void removeConfigHooks()
111 | {
112 | import std.algorithm : startsWith;
113 |
114 | foreach (key; _configHooks.byKey)
115 | {
116 | if (key.startsWith(this.toString() ~ '/'))
117 | {
118 | _configHooks.remove(key);
119 | }
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/source/dls/updater.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.updater;
22 |
23 | private immutable changelogUrl = "https://github.com/d-language-server/dls/blob/v%s/CHANGELOG.md";
24 |
25 | void cleanup()
26 | {
27 | import dls.bootstrap : dubBinDir;
28 | import dls.info : currentVersion;
29 | import dub.semver : compareVersions;
30 | import std.file : FileException, SpanMode, dirEntries, isSymlink, remove, rmdirRecurse;
31 | import std.path : baseName;
32 | import std.regex : matchFirst;
33 |
34 | foreach (string entry; dirEntries(dubBinDir, SpanMode.shallow))
35 | {
36 | const match = entry.baseName.matchFirst(`dls-v([\d.]+)`);
37 |
38 | if (match)
39 | {
40 | if (compareVersions(currentVersion, match[1]) > 0)
41 | {
42 | try
43 | {
44 | rmdirRecurse(entry);
45 | }
46 | catch (FileException e)
47 | {
48 | }
49 | }
50 | }
51 | else if (isSymlink(entry))
52 | {
53 | try
54 | {
55 | version (Windows)
56 | {
57 | import std.file : isDir, rmdir;
58 | import std.stdio : File;
59 |
60 | if (isDir(entry))
61 | {
62 | try
63 | {
64 | dirEntries(entry, SpanMode.shallow);
65 | }
66 | catch (FileException e)
67 | {
68 | rmdir(entry);
69 | }
70 | }
71 | else
72 | {
73 | try
74 | {
75 | File(entry, "rb");
76 | }
77 | catch (Exception e)
78 | {
79 | remove(entry);
80 | }
81 | }
82 | }
83 | else version (Posix)
84 | {
85 | import std.file : exists, readLink;
86 |
87 | if (!exists(readLink(entry)))
88 | {
89 | remove(entry);
90 | }
91 | }
92 | else
93 | {
94 | static assert(false, "Platform not supported");
95 | }
96 | }
97 | catch (Exception e)
98 | {
99 | }
100 | }
101 | }
102 | }
103 |
104 | void update(bool autoUpdate, bool preReleaseBuilds)
105 | {
106 | import core.time : hours;
107 | import dls.bootstrap : UpgradeFailedException, canDownloadDls, downloadDls,
108 | allReleases, linkDls;
109 | import dls.info : currentVersion;
110 | static import dls.protocol.jsonrpc;
111 | import dls.protocol.interfaces.dls : DlsUpgradeSizeParams, TranslationParams;
112 | import dls.protocol.logger : logger;
113 | import dls.protocol.messages.methods : Dls;
114 | import dls.protocol.messages.window : Util;
115 | import dls.util.i18n : Tr;
116 | import dub.semver : compareVersions;
117 | import std.algorithm : filter, stripLeft;
118 | import std.concurrency : ownerTid, receiveOnly, register, send, thisTid;
119 | import std.datetime : Clock, SysTime;
120 | import std.format : format;
121 | import std.json : JSONType;
122 |
123 | auto validReleases = allReleases.filter!(
124 | r => r["prerelease"].type == JSONType.false_ || preReleaseBuilds);
125 |
126 | if (validReleases.empty)
127 | {
128 | logger.warning("Unable to find any valid release");
129 | return;
130 | }
131 |
132 | immutable latestRelease = validReleases.front;
133 | immutable latestVersion = latestRelease["tag_name"].str.stripLeft('v');
134 | immutable releaseTime = SysTime.fromISOExtString(latestRelease["published_at"].str);
135 |
136 | if (latestVersion.length == 0 || compareVersions(currentVersion,
137 | latestVersion) >= 0 || (Clock.currTime.toUTC() - releaseTime < 1.hours))
138 | {
139 | return;
140 | }
141 |
142 | if (!autoUpdate)
143 | {
144 | auto id = Util.sendMessageRequest(Tr.app_upgradeDls,
145 | [Tr.app_upgradeDls_upgrade], [latestVersion, currentVersion]);
146 | immutable threadName = "updater";
147 | register(threadName, thisTid());
148 | send(ownerTid(), Util.ThreadMessageData(id, Tr.app_upgradeDls, threadName));
149 |
150 | immutable shouldUpgrade = receiveOnly!bool();
151 |
152 | if (!shouldUpgrade)
153 | {
154 | return;
155 | }
156 | }
157 |
158 | dls.protocol.jsonrpc.send(Dls.UpgradeDls.didStart,
159 | new TranslationParams(Tr.app_upgradeDls_upgrading));
160 |
161 | scope (exit)
162 | {
163 | dls.protocol.jsonrpc.send(Dls.UpgradeDls.didStop);
164 | }
165 |
166 | bool upgradeSuccessful;
167 |
168 | if (canDownloadDls)
169 | {
170 | try
171 | {
172 | enum totalSizeCallback = (size_t size) {
173 | dls.protocol.jsonrpc.send(Dls.UpgradeDls.didChangeTotalSize,
174 | new DlsUpgradeSizeParams(Tr.app_upgradeDls_downloading, [], size));
175 | };
176 | enum chunkSizeCallback = (size_t size) {
177 | dls.protocol.jsonrpc.send(Dls.UpgradeDls.didChangeCurrentSize,
178 | new DlsUpgradeSizeParams(Tr.app_upgradeDls_downloading, [], size));
179 | };
180 | enum extractCallback = () {
181 | dls.protocol.jsonrpc.send(Dls.UpgradeDls.didExtract,
182 | new TranslationParams(Tr.app_upgradeDls_extracting));
183 | };
184 |
185 | downloadDls(totalSizeCallback, chunkSizeCallback, extractCallback);
186 | upgradeSuccessful = true;
187 | }
188 | catch (Exception e)
189 | {
190 | logger.error("Could not download DLS: %s", e.msg);
191 | Util.sendMessage(Tr.app_upgradeDls_downloadError);
192 | }
193 | }
194 |
195 | try
196 | {
197 | linkDls();
198 | auto id = Util.sendMessageRequest(Tr.app_showChangelog,
199 | [Tr.app_showChangelog_show], [latestVersion]);
200 | send(ownerTid(), Util.ThreadMessageData(id, Tr.app_showChangelog,
201 | format!changelogUrl(latestVersion)));
202 | }
203 | catch (UpgradeFailedException e)
204 | {
205 | logger.error("Could not symlink DLS: %s", e.msg);
206 | Util.sendMessage(Tr.app_upgradeDls_linkError);
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/source/main.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | int main(string[] args)
22 | {
23 | import dls.bootstrap : networkBackend;
24 | import dls.info : buildArch, buildPlatform, buildType, compilerVersion, currentVersion;
25 | import dls.protocol.interfaces : InitializeParams;
26 | import dls.protocol.state : initOptions;
27 | import dls.server : Server;
28 | import dls.util.communicator : SocketCommunicator, StdioCommunicator, communicator;
29 | import dls.util.getopt : printHelp;
30 | import dls.util.i18n : Tr, tr;
31 | import std.ascii : newline;
32 | import std.compiler : name;
33 | import std.conv : text;
34 | import std.getopt : config, getopt;
35 |
36 | bool stdio = true;
37 | ushort port;
38 | bool version_;
39 | auto init = new InitializeParams.InitializationOptions();
40 |
41 | //dfmt off
42 | auto result = getopt(args, config.passThrough,
43 | "stdio", tr(Tr.app_help_stdio), &stdio,
44 | "socket", tr(Tr.app_help_socket), &port,
45 | "tcp", tr(Tr.app_help_socket), &port,
46 | "version", tr(Tr.app_help_version), &version_,
47 | "init.autoUpdate", &init.autoUpdate,
48 | "init.preReleaseBuilds", &init.preReleaseBuilds,
49 | "init.safeMode", &init.safeMode,
50 | "init.catchErrors", &init.catchErrors,
51 | "init.logFile", &init.logFile,
52 | "init.capabilities.hover", &init.capabilities.hover,
53 | "init.capabilities.completion", &init.capabilities.completion,
54 | "init.capabilities.definition", &init.capabilities.definition,
55 | "init.capabilities.typeDefinition", &init.capabilities.typeDefinition,
56 | "init.capabilities.references", &init.capabilities.references,
57 | "init.capabilities.documentHighlight", &init.capabilities.documentHighlight,
58 | "init.capabilities.documentSymbol", &init.capabilities.documentSymbol,
59 | "init.capabilities.workspaceSymbol", &init.capabilities.workspaceSymbol,
60 | "init.capabilities.codeAction", &init.capabilities.codeAction,
61 | "init.capabilities.documentFormatting", &init.capabilities.documentFormatting,
62 | "init.capabilities.documentRangeFormatting", &init.capabilities.documentRangeFormatting,
63 | "init.capabilities.documentOnTypeFormatting", &init.capabilities.documentOnTypeFormatting,
64 | "init.capabilities.rename", &init.capabilities.rename,
65 | "init.symbol.autoImports", &init.symbol.autoImports);
66 | //dfmt on
67 |
68 | initOptions = init;
69 |
70 | if (result.helpWanted)
71 | {
72 | communicator = new StdioCommunicator(false);
73 | printHelp(tr(Tr.app_help_title), result.options, data => communicator.write(data));
74 | communicator.flush();
75 | return 0;
76 | }
77 | else if (version_)
78 | {
79 | communicator = new StdioCommunicator(false);
80 | communicator.write(tr(Tr.app_version_dlsVersion, [currentVersion,
81 | buildPlatform, buildArch, buildType, networkBackend]));
82 | communicator.write(newline);
83 | communicator.write(tr(Tr.app_version_compilerVersion, [name,
84 | compilerVersion, text(__VERSION__)]));
85 | communicator.write(newline);
86 | communicator.flush();
87 | return 0;
88 | }
89 | else if (port > 0)
90 | {
91 | communicator = new SocketCommunicator(port);
92 | }
93 | else if (stdio)
94 | {
95 | communicator = new StdioCommunicator(true);
96 | }
97 | else
98 | {
99 | return -1;
100 | }
101 |
102 | Server.loop();
103 | return Server.initialized ? 1 : 0;
104 | }
105 |
--------------------------------------------------------------------------------
/tests/.gitattributes:
--------------------------------------------------------------------------------
1 | */source/*.d eol=lf
2 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 | *.out.json
2 |
--------------------------------------------------------------------------------
/tests/formatting/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "formatting"
3 | }
--------------------------------------------------------------------------------
/tests/formatting/messages/_order.txt:
--------------------------------------------------------------------------------
1 | initialize
2 | initialized
3 | textDocument_formatting
4 | textDocument_rangeFormatting
5 | textDocument_onTypeFormatting
6 | shutdown
7 |
--------------------------------------------------------------------------------
/tests/formatting/messages/initialize.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "initialize",
4 | "method": "initialize",
5 | "params": {
6 | "processId": null,
7 | "rootUri": "testFile:///",
8 | "capabilities": {
9 | "textDocument": {
10 | "formatting": {
11 | "dynamicRegistration": false
12 | },
13 | "rangeFormatting": {
14 | "dynamicRegistration": false
15 | },
16 | "onTypeFormatting": {
17 | "dynamicRegistration": false
18 | }
19 | }
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/tests/formatting/messages/initialize.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "initialize",
4 | "jsonrpc": "2.0",
5 | "result": {
6 | "capabilities": {
7 | "documentFormattingProvider": true,
8 | "documentOnTypeFormattingProvider": {
9 | "firstTriggerCharacter": ";"
10 | },
11 | "documentRangeFormattingProvider": true,
12 | "textDocumentSync": {
13 | "change": 2,
14 | "openClose": true,
15 | "save": {
16 | "includeText": false
17 | },
18 | "willSave": true,
19 | "willSaveWaitUntil": false
20 | }
21 | }
22 | }
23 | }
24 | ]
--------------------------------------------------------------------------------
/tests/formatting/messages/initialized.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "method": "initialized",
4 | "params": {}
5 | }
--------------------------------------------------------------------------------
/tests/formatting/messages/initialized.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "jsonrpc": "2.0",
4 | "method": "textDocument/publishDiagnostics",
5 | "params": {
6 | "diagnostics": [],
7 | "uri": "testFile:///source/app.d"
8 | }
9 | }
10 | ]
--------------------------------------------------------------------------------
/tests/formatting/messages/shutdown.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "shutdown",
4 | "method": "shutdown",
5 | "params": {}
6 | }
--------------------------------------------------------------------------------
/tests/formatting/messages/shutdown.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "shutdown",
4 | "jsonrpc": "2.0",
5 | "result": null
6 | }
7 | ]
--------------------------------------------------------------------------------
/tests/formatting/messages/textDocument_formatting.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/formatting",
4 | "method": "textDocument/formatting",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "options": {
10 | "tabSize": 4,
11 | "insertSpaces": true
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/tests/formatting/messages/textDocument_formatting.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/formatting",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "newText": "\n\n",
8 | "range": {
9 | "end": {
10 | "character": 17,
11 | "line": 0
12 | },
13 | "start": {
14 | "character": 17,
15 | "line": 0
16 | }
17 | }
18 | },
19 | {
20 | "newText": "\n",
21 | "range": {
22 | "end": {
23 | "character": 28,
24 | "line": 0
25 | },
26 | "start": {
27 | "character": 28,
28 | "line": 0
29 | }
30 | }
31 | },
32 | {
33 | "newText": "\n ",
34 | "range": {
35 | "end": {
36 | "character": 29,
37 | "line": 0
38 | },
39 | "start": {
40 | "character": 29,
41 | "line": 0
42 | }
43 | }
44 | },
45 | {
46 | "newText": "\n",
47 | "range": {
48 | "end": {
49 | "character": 1,
50 | "line": 1
51 | },
52 | "start": {
53 | "character": 1,
54 | "line": 1
55 | }
56 | }
57 | }
58 | ]
59 | }
60 | ]
--------------------------------------------------------------------------------
/tests/formatting/messages/textDocument_onTypeFormatting.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/onTypeFormatting",
4 | "method": "textDocument/onTypeFormatting",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 0,
11 | "character": 81
12 | },
13 | "ch": ";",
14 | "options": {
15 | "tabSize": 4,
16 | "insertSpaces": true
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/formatting/messages/textDocument_onTypeFormatting.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/onTypeFormatting",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "newText": "\n\n",
8 | "range": {
9 | "end": {
10 | "character": 17,
11 | "line": 0
12 | },
13 | "start": {
14 | "character": 17,
15 | "line": 0
16 | }
17 | }
18 | },
19 | {
20 | "newText": "\n",
21 | "range": {
22 | "end": {
23 | "character": 28,
24 | "line": 0
25 | },
26 | "start": {
27 | "character": 28,
28 | "line": 0
29 | }
30 | }
31 | },
32 | {
33 | "newText": "\n ",
34 | "range": {
35 | "end": {
36 | "character": 29,
37 | "line": 0
38 | },
39 | "start": {
40 | "character": 29,
41 | "line": 0
42 | }
43 | }
44 | }
45 | ]
46 | }
47 | ]
--------------------------------------------------------------------------------
/tests/formatting/messages/textDocument_rangeFormatting.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/rangeFormatting",
4 | "method": "textDocument/rangeFormatting",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "range": {
10 | "start": {
11 | "line": 0,
12 | "character": 24
13 | },
14 | "end": {
15 | "line": 0,
16 | "character": 32
17 | }
18 | },
19 | "options": {
20 | "tabSize": 4,
21 | "insertSpaces": true
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/formatting/messages/textDocument_rangeFormatting.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/rangeFormatting",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "newText": "\n",
8 | "range": {
9 | "end": {
10 | "character": 28,
11 | "line": 0
12 | },
13 | "start": {
14 | "character": 28,
15 | "line": 0
16 | }
17 | }
18 | },
19 | {
20 | "newText": "\n ",
21 | "range": {
22 | "end": {
23 | "character": 29,
24 | "line": 0
25 | },
26 | "start": {
27 | "character": 29,
28 | "line": 0
29 | }
30 | }
31 | }
32 | ]
33 | }
34 | ]
--------------------------------------------------------------------------------
/tests/formatting/source/app.d:
--------------------------------------------------------------------------------
1 | import std.stdio;void main(){writeln("Edit source/app.d to start your project.");
2 | }
--------------------------------------------------------------------------------
/tests/hello/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello"
3 | }
--------------------------------------------------------------------------------
/tests/hello/messages/_order.txt:
--------------------------------------------------------------------------------
1 | initialize
2 | initialized
3 | shutdown
4 |
--------------------------------------------------------------------------------
/tests/hello/messages/initialize.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "initialize",
4 | "method": "initialize",
5 | "params": {
6 | "processId": null,
7 | "rootUri": "testFile:///",
8 | "capabilities": {}
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/hello/messages/initialize.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "initialize",
4 | "jsonrpc": "2.0",
5 | "result": {
6 | "capabilities": {
7 | "textDocumentSync": {
8 | "change": 2,
9 | "openClose": true,
10 | "save": {
11 | "includeText": false
12 | },
13 | "willSave": true,
14 | "willSaveWaitUntil": false
15 | }
16 | }
17 | }
18 | }
19 | ]
--------------------------------------------------------------------------------
/tests/hello/messages/initialized.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "method": "initialized",
4 | "params": {}
5 | }
--------------------------------------------------------------------------------
/tests/hello/messages/initialized.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "jsonrpc": "2.0",
4 | "method": "textDocument/publishDiagnostics",
5 | "params": {
6 | "diagnostics": [],
7 | "uri": "testFile:///source/app.d"
8 | }
9 | }
10 | ]
--------------------------------------------------------------------------------
/tests/hello/messages/shutdown.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "shutdown",
4 | "method": "shutdown",
5 | "params": {}
6 | }
--------------------------------------------------------------------------------
/tests/hello/messages/shutdown.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "shutdown",
4 | "jsonrpc": "2.0",
5 | "result": null
6 | }
7 | ]
--------------------------------------------------------------------------------
/tests/hello/source/app.d:
--------------------------------------------------------------------------------
1 | import std.stdio;
2 |
3 | void main()
4 | {
5 | writeln("Edit source/app.d to start your project.");
6 | }
7 |
--------------------------------------------------------------------------------
/tests/main.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | import dls.util.communicator : Communicator;
22 | import std.json : JSONValue;
23 |
24 | private immutable orderFileName = "_order";
25 |
26 | private enum MessageFileType : string
27 | {
28 | order = ".txt",
29 | input = ".in.json",
30 | output = ".out.json",
31 | reference = ".ref.json"
32 | }
33 |
34 | private struct Message
35 | {
36 | private string name;
37 | private char[] content;
38 | }
39 |
40 | private class TestCommunicator : Communicator
41 | {
42 | string[] testedDirectories;
43 | private Message[][string] _directoriesMessages;
44 | private string _directory;
45 | private string _lastDirectory;
46 | private string _lastReadMessageName;
47 | private string _currentOutput;
48 | private JSONValue[] _outputMessages;
49 |
50 | this()
51 | {
52 | import std.algorithm : filter;
53 | import std.file : SpanMode, dirEntries, getcwd, isDir;
54 | import std.path : buildPath;
55 |
56 | foreach (directory; dirEntries(buildPath(getcwd(), "tests"), SpanMode.shallow).filter!(
57 | entry => isDir(entry.name) && hasMessages(entry.name)))
58 | {
59 | _directoriesMessages[directory] = [];
60 |
61 | if (_directory.length == 0)
62 | {
63 | _directory = directory;
64 | }
65 | }
66 |
67 | fillDirectoryMessages();
68 | }
69 |
70 | bool hasData()
71 | {
72 | return _directoriesMessages.keys.length > 0;
73 | }
74 |
75 | bool hasPendingData()
76 | {
77 | return false;
78 | }
79 |
80 | char[] read(const size_t size)
81 | {
82 | auto currentMessage = _directoriesMessages[_directory][0];
83 | auto result = currentMessage.content[0 .. size];
84 |
85 | if (currentMessage.content.length > size)
86 | {
87 | _directoriesMessages[_directory][0].content = currentMessage.content[size .. $];
88 | }
89 | else
90 | {
91 | _lastDirectory = _directory;
92 | _lastReadMessageName = currentMessage.name;
93 | _outputMessages = [];
94 |
95 | if (_directoriesMessages[_directory].length > 1)
96 | {
97 | _directoriesMessages[_directory] = _directoriesMessages[_directory][1 .. $];
98 | }
99 | else
100 | {
101 | testedDirectories ~= _directory;
102 | _directoriesMessages.remove(_directory);
103 |
104 | if (_directoriesMessages.keys.length > 0)
105 | {
106 | _directory = _directoriesMessages.keys[0];
107 | fillDirectoryMessages();
108 | }
109 | }
110 | }
111 |
112 | return result;
113 | }
114 |
115 | void write(const char[] buffer)
116 | {
117 | _currentOutput ~= buffer;
118 | }
119 |
120 | void flush()
121 | {
122 | import std.algorithm : findSkip;
123 | import std.file : write;
124 | import std.json : JSONOptions, parseJSON;
125 |
126 | _currentOutput.findSkip("\r\n\r\n");
127 | _outputMessages ~= parseJSON(_currentOutput);
128 | const outputPath = getMessagePath(_lastDirectory, _lastReadMessageName,
129 | MessageFileType.output);
130 | write(outputPath, JSONValue(_outputMessages)
131 | .toPrettyString(JSONOptions.doNotEscapeSlashes));
132 | _currentOutput = "";
133 | }
134 |
135 | private void fillDirectoryMessages()
136 | {
137 | import std.file : readText;
138 | import std.format : format;
139 |
140 | foreach (line; getOrderedMessageNames(_directory))
141 | {
142 | const inputPath = getMessagePath(_directory, line, MessageFileType.input);
143 | auto input = readText!(char[])(inputPath).expandTestUris(_directory);
144 | auto message = Message(line);
145 | message.content ~= format("Content-Length: %s\r\n\r\n%s", input.length, input);
146 | _directoriesMessages[_directory] ~= message;
147 | }
148 | }
149 | }
150 |
151 | int main()
152 | {
153 | import dls.server : Server;
154 | import dls.util.communicator : communicator;
155 |
156 | auto testCommunicator = new TestCommunicator();
157 | communicator = testCommunicator;
158 | Server.loop();
159 | return checkResults(testCommunicator.testedDirectories);
160 | }
161 |
162 | private int checkResults(const string[] directories)
163 | {
164 | import std.algorithm : reduce;
165 | import std.array : array;
166 | import std.format : format;
167 | import std.json : JSONOptions;
168 | import std.range : repeat;
169 | import std.stdio : stderr;
170 |
171 | static void writeHeader(const string header)
172 | {
173 | auto headerLine = repeat('=', header.length);
174 | stderr.writeln(headerLine);
175 | stderr.writeln(header);
176 | stderr.writeln(headerLine);
177 | }
178 |
179 | size_t testCount;
180 | size_t passCount;
181 |
182 | foreach (directory; directories)
183 | {
184 | writeHeader(format("Test directory %s", directory));
185 |
186 | auto orderedMessageNames = getOrderedMessageNames(directory).array;
187 | const maxNameLength = reduce!((a, b) => a.length > b.length ? a : b)("",
188 | orderedMessageNames).length;
189 |
190 | foreach (name; orderedMessageNames)
191 | {
192 | auto output = getJSON(directory, name, MessageFileType.output);
193 | auto reference = getJSON(directory, name, MessageFileType.reference);
194 | stderr.writef(" * Message %s%s: ", name, repeat(' ', maxNameLength - name.length));
195 | ++testCount;
196 |
197 | if (output != reference)
198 | {
199 | stderr.writeln("FAIL \u2718");
200 | writeDiff(reference.toPrettyString(JSONOptions.doNotEscapeSlashes),
201 | output.toPrettyString(JSONOptions.doNotEscapeSlashes));
202 | }
203 | else
204 | {
205 | stderr.writeln("PASS \u2714");
206 | ++passCount;
207 | }
208 | }
209 | }
210 |
211 | writeHeader(format("Passed message tests: %s/%s", passCount, testCount));
212 | return passCount == testCount ? 0 : 1;
213 | }
214 |
215 | private bool hasMessages(const string directory)
216 | {
217 | import std.file : exists;
218 |
219 | return exists(getMessagePath(directory, orderFileName, MessageFileType.order));
220 | }
221 |
222 | private auto getOrderedMessageNames(const string directory)
223 | {
224 | import std.algorithm : map;
225 | import std.stdio : File;
226 | import std.string : strip;
227 |
228 | return File(getMessagePath(directory, orderFileName, MessageFileType.order), "r")
229 | .byLineCopy.map!strip;
230 | }
231 |
232 | private JSONValue getJSON(const string directory, const string name, const MessageFileType type)
233 | {
234 | import std.algorithm : sort;
235 | import std.array : array;
236 | import std.json : JSONValue, parseJSON;
237 | import std.file : exists, readText;
238 |
239 | const path = getMessagePath(directory, name, type);
240 | auto rawJSON = parseJSON(exists(path) ? readText(path).expandTestUris(directory) : "[]");
241 | return JSONValue(rawJSON.array.sort!((a, b) => a.toString() < b.toString()).array);
242 | }
243 |
244 | private string getMessagePath(const string directory, const string name, const MessageFileType type)
245 | {
246 | import std.path : buildPath;
247 |
248 | return buildPath(directory, "messages", name ~ type);
249 | }
250 |
251 | private inout(char[]) expandTestUris(inout(char[]) text, const string directory)
252 | {
253 | import dls.util.uri : Uri;
254 | import std.array : replace;
255 |
256 | return text.replace("testFile://", Uri.fromPath(directory).toString());
257 | }
258 |
259 | private void writeDiff(const string reference, const string output)
260 | {
261 | import std.ascii : newline;
262 | import std.conv : text;
263 | import std.file : remove, tempDir, write;
264 | import std.path : buildPath;
265 | import std.process : Config, execute;
266 | import std.stdio : stderr;
267 | import std.uuid : randomUUID;
268 |
269 | const mainPath = buildPath(tempDir, randomUUID().toString());
270 | const refPath = mainPath ~ MessageFileType.reference;
271 | const outPath = mainPath ~ MessageFileType.output;
272 |
273 | write(refPath, reference);
274 | write(outPath, output);
275 |
276 | scope (exit)
277 | {
278 | remove(refPath);
279 | remove(outPath);
280 | }
281 |
282 | version (Windows)
283 | {
284 | const args = ["fc.exe", refPath, outPath];
285 | }
286 | else version (Posix)
287 | {
288 | static immutable maxSize = text(size_t.max);
289 | const args = ["diff", "-U", maxSize, refPath, outPath];
290 | }
291 | else
292 | {
293 | string[] args;
294 | }
295 |
296 | stderr.write(args.length > 0 ? execute(args, null, Config.suppressConsole)
297 | .output : "No diff output available on this platform" ~ newline);
298 | }
299 |
--------------------------------------------------------------------------------
/tests/references/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "references"
3 | }
--------------------------------------------------------------------------------
/tests/references/messages/_order.txt:
--------------------------------------------------------------------------------
1 | initialize
2 | initialized
3 | textDocument_references_1
4 | textDocument_references_1_noDeclaration
5 | textDocument_references_2
6 | textDocument_references_2_noDeclaration
7 | textDocument_references_3
8 | textDocument_references_3_noDeclaration
9 | textDocument_references_4
10 | textDocument_references_4_noDeclaration
11 | shutdown
12 |
--------------------------------------------------------------------------------
/tests/references/messages/initialize.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "initialize",
4 | "method": "initialize",
5 | "params": {
6 | "processId": null,
7 | "rootUri": "testFile:///",
8 | "capabilities": {
9 | "textDocument": {
10 | "references": {
11 | "dynamicRegistration": false
12 | }
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/references/messages/initialize.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "initialize",
4 | "jsonrpc": "2.0",
5 | "result": {
6 | "capabilities": {
7 | "referencesProvider": true,
8 | "textDocumentSync": {
9 | "change": 2,
10 | "openClose": true,
11 | "save": {
12 | "includeText": false
13 | },
14 | "willSave": true,
15 | "willSaveWaitUntil": false
16 | }
17 | }
18 | }
19 | }
20 | ]
--------------------------------------------------------------------------------
/tests/references/messages/initialized.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "method": "initialized",
4 | "params": {}
5 | }
--------------------------------------------------------------------------------
/tests/references/messages/initialized.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "jsonrpc": "2.0",
4 | "method": "textDocument/publishDiagnostics",
5 | "params": {
6 | "diagnostics": [
7 | {
8 | "code": "dscanner.style.undocumented_declaration",
9 | "message": "Public declaration 'Foo' is undocumented.",
10 | "range": {
11 | "end": {
12 | "character": 9,
13 | "line": 2
14 | },
15 | "start": {
16 | "character": 6,
17 | "line": 2
18 | }
19 | },
20 | "severity": 2,
21 | "source": "D-Scanner"
22 | },
23 | {
24 | "code": "dscanner.style.undocumented_declaration",
25 | "message": "Public declaration 'member' is undocumented.",
26 | "range": {
27 | "end": {
28 | "character": 14,
29 | "line": 4
30 | },
31 | "start": {
32 | "character": 8,
33 | "line": 4
34 | }
35 | },
36 | "severity": 2,
37 | "source": "D-Scanner"
38 | }
39 | ],
40 | "uri": "testFile:///source/app.d"
41 | }
42 | }
43 | ]
--------------------------------------------------------------------------------
/tests/references/messages/shutdown.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "shutdown",
4 | "method": "shutdown",
5 | "params": {}
6 | }
--------------------------------------------------------------------------------
/tests/references/messages/shutdown.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "shutdown",
4 | "jsonrpc": "2.0",
5 | "result": null
6 | }
7 | ]
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_1.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/references 1",
4 | "method": "textDocument/references",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 9,
11 | "character": 20
12 | },
13 | "context": {
14 | "includeDeclaration": true
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_1.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/references 1",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "range": {
8 | "end": {
9 | "character": 9,
10 | "line": 2
11 | },
12 | "start": {
13 | "character": 6,
14 | "line": 2
15 | }
16 | },
17 | "uri": "testFile:///source/app.d"
18 | },
19 | {
20 | "range": {
21 | "end": {
22 | "character": 22,
23 | "line": 9
24 | },
25 | "start": {
26 | "character": 19,
27 | "line": 9
28 | }
29 | },
30 | "uri": "testFile:///source/app.d"
31 | }
32 | ]
33 | }
34 | ]
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_1_noDeclaration.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/references 1 no declaration",
4 | "method": "textDocument/references",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 9,
11 | "character": 20
12 | },
13 | "context": {
14 | "includeDeclaration": false
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_1_noDeclaration.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/references 1 no declaration",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "range": {
8 | "end": {
9 | "character": 22,
10 | "line": 9
11 | },
12 | "start": {
13 | "character": 19,
14 | "line": 9
15 | }
16 | },
17 | "uri": "testFile:///source/app.d"
18 | }
19 | ]
20 | }
21 | ]
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_2.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/references 2",
4 | "method": "textDocument/references",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 10,
11 | "character": 8
12 | },
13 | "context": {
14 | "includeDeclaration": true
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_2.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/references 2",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "range": {
8 | "end": {
9 | "character": 11,
10 | "line": 10
11 | },
12 | "start": {
13 | "character": 4,
14 | "line": 10
15 | }
16 | },
17 | "uri": "testFile:///source/app.d"
18 | },
19 | {
20 | "range": {
21 | "end": {
22 | "character": 11,
23 | "line": 12
24 | },
25 | "start": {
26 | "character": 4,
27 | "line": 12
28 | }
29 | },
30 | "uri": "testFile:///source/app.d"
31 | }
32 | ]
33 | }
34 | ]
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_2_noDeclaration.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/references 2 no declaration",
4 | "method": "textDocument/references",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 10,
11 | "character": 8
12 | },
13 | "context": {
14 | "includeDeclaration": false
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_2_noDeclaration.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/references 2 no declaration",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "range": {
8 | "end": {
9 | "character": 11,
10 | "line": 10
11 | },
12 | "start": {
13 | "character": 4,
14 | "line": 10
15 | }
16 | },
17 | "uri": "testFile:///source/app.d"
18 | },
19 | {
20 | "range": {
21 | "end": {
22 | "character": 11,
23 | "line": 12
24 | },
25 | "start": {
26 | "character": 4,
27 | "line": 12
28 | }
29 | },
30 | "uri": "testFile:///source/app.d"
31 | }
32 | ]
33 | }
34 | ]
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_3.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/references 3",
4 | "method": "textDocument/references",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 10,
11 | "character": 14
12 | },
13 | "context": {
14 | "includeDeclaration": true
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_3.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/references 3",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "range": {
8 | "end": {
9 | "character": 12,
10 | "line": 9
11 | },
12 | "start": {
13 | "character": 9,
14 | "line": 9
15 | }
16 | },
17 | "uri": "testFile:///source/app.d"
18 | },
19 | {
20 | "range": {
21 | "end": {
22 | "character": 15,
23 | "line": 10
24 | },
25 | "start": {
26 | "character": 12,
27 | "line": 10
28 | }
29 | },
30 | "uri": "testFile:///source/app.d"
31 | },
32 | {
33 | "range": {
34 | "end": {
35 | "character": 7,
36 | "line": 11
37 | },
38 | "start": {
39 | "character": 4,
40 | "line": 11
41 | }
42 | },
43 | "uri": "testFile:///source/app.d"
44 | },
45 | {
46 | "range": {
47 | "end": {
48 | "character": 15,
49 | "line": 12
50 | },
51 | "start": {
52 | "character": 12,
53 | "line": 12
54 | }
55 | },
56 | "uri": "testFile:///source/app.d"
57 | }
58 | ]
59 | }
60 | ]
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_3_noDeclaration.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/references 3 no declaration",
4 | "method": "textDocument/references",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 10,
11 | "character": 14
12 | },
13 | "context": {
14 | "includeDeclaration": false
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_3_noDeclaration.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/references 3 no declaration",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "range": {
8 | "end": {
9 | "character": 15,
10 | "line": 10
11 | },
12 | "start": {
13 | "character": 12,
14 | "line": 10
15 | }
16 | },
17 | "uri": "testFile:///source/app.d"
18 | },
19 | {
20 | "range": {
21 | "end": {
22 | "character": 7,
23 | "line": 11
24 | },
25 | "start": {
26 | "character": 4,
27 | "line": 11
28 | }
29 | },
30 | "uri": "testFile:///source/app.d"
31 | },
32 | {
33 | "range": {
34 | "end": {
35 | "character": 15,
36 | "line": 12
37 | },
38 | "start": {
39 | "character": 12,
40 | "line": 12
41 | }
42 | },
43 | "uri": "testFile:///source/app.d"
44 | }
45 | ]
46 | }
47 | ]
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_4.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/references 4",
4 | "method": "textDocument/references",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 12,
11 | "character": 20
12 | },
13 | "context": {
14 | "includeDeclaration": true
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_4.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/references 4",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "range": {
8 | "end": {
9 | "character": 14,
10 | "line": 4
11 | },
12 | "start": {
13 | "character": 8,
14 | "line": 4
15 | }
16 | },
17 | "uri": "testFile:///source/app.d"
18 | },
19 | {
20 | "range": {
21 | "end": {
22 | "character": 22,
23 | "line": 10
24 | },
25 | "start": {
26 | "character": 16,
27 | "line": 10
28 | }
29 | },
30 | "uri": "testFile:///source/app.d"
31 | },
32 | {
33 | "range": {
34 | "end": {
35 | "character": 14,
36 | "line": 11
37 | },
38 | "start": {
39 | "character": 8,
40 | "line": 11
41 | }
42 | },
43 | "uri": "testFile:///source/app.d"
44 | },
45 | {
46 | "range": {
47 | "end": {
48 | "character": 22,
49 | "line": 12
50 | },
51 | "start": {
52 | "character": 16,
53 | "line": 12
54 | }
55 | },
56 | "uri": "testFile:///source/app.d"
57 | }
58 | ]
59 | }
60 | ]
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_4_noDeclaration.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/references 4 no declaration",
4 | "method": "textDocument/references",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | },
9 | "position": {
10 | "line": 12,
11 | "character": 20
12 | },
13 | "context": {
14 | "includeDeclaration": false
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/tests/references/messages/textDocument_references_4_noDeclaration.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/references 4 no declaration",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "range": {
8 | "end": {
9 | "character": 22,
10 | "line": 10
11 | },
12 | "start": {
13 | "character": 16,
14 | "line": 10
15 | }
16 | },
17 | "uri": "testFile:///source/app.d"
18 | },
19 | {
20 | "range": {
21 | "end": {
22 | "character": 14,
23 | "line": 11
24 | },
25 | "start": {
26 | "character": 8,
27 | "line": 11
28 | }
29 | },
30 | "uri": "testFile:///source/app.d"
31 | },
32 | {
33 | "range": {
34 | "end": {
35 | "character": 22,
36 | "line": 12
37 | },
38 | "start": {
39 | "character": 16,
40 | "line": 12
41 | }
42 | },
43 | "uri": "testFile:///source/app.d"
44 | }
45 | ]
46 | }
47 | ]
--------------------------------------------------------------------------------
/tests/references/source/app.d:
--------------------------------------------------------------------------------
1 | import std.stdio;
2 |
3 | class Foo
4 | {
5 | int member;
6 | }
7 |
8 | void main()
9 | {
10 | auto foo = new Foo();
11 | writeln(foo.member);
12 | foo.member += 42;
13 | writeln(foo.member);
14 | }
15 |
--------------------------------------------------------------------------------
/tests/symbol/dscanner.ini:
--------------------------------------------------------------------------------
1 | ; Configure which static analysis checks are enabled
2 | [analysis.config.StaticAnalysisConfig]
3 | ; Check variable, class, struct, interface, union, and function names against t
4 | ; he Phobos style guide
5 | style_check="enabled"
6 | ; Check for array literals that cause unnecessary allocation
7 | enum_array_literal_check="enabled"
8 | ; Check for poor exception handling practices
9 | exception_check="enabled"
10 | ; Check for use of the deprecated 'delete' keyword
11 | delete_check="enabled"
12 | ; Check for use of the deprecated floating point operators
13 | float_operator_check="enabled"
14 | ; Check number literals for readability
15 | number_style_check="enabled"
16 | ; Checks that opEquals, opCmp, toHash, and toString are either const, immutable
17 | ; , or inout.
18 | object_const_check="enabled"
19 | ; Checks for .. expressions where the left side is larger than the right.
20 | backwards_range_check="enabled"
21 | ; Checks for if statements whose 'then' block is the same as the 'else' block
22 | if_else_same_check="enabled"
23 | ; Checks for some problems with constructors
24 | constructor_check="enabled"
25 | ; Checks for unused variables and function parameters
26 | unused_variable_check="disabled"
27 | ; Checks for unused labels
28 | unused_label_check="enabled"
29 | ; Checks for duplicate attributes
30 | duplicate_attribute="enabled"
31 | ; Checks that opEquals and toHash are both defined or neither are defined
32 | opequals_tohash_check="enabled"
33 | ; Checks for subtraction from .length properties
34 | length_subtraction_check="enabled"
35 | ; Checks for methods or properties whose names conflict with built-in propertie
36 | ; s
37 | builtin_property_names_check="enabled"
38 | ; Checks for confusing code in inline asm statements
39 | asm_style_check="enabled"
40 | ; Checks for confusing logical operator precedence
41 | logical_precedence_check="enabled"
42 | ; Checks for undocumented public declarations
43 | undocumented_declaration_check="disabled"
44 | ; Checks for poor placement of function attributes
45 | function_attribute_check="enabled"
46 | ; Checks for use of the comma operator
47 | comma_expression_check="enabled"
48 | ; Checks for local imports that are too broad
49 | local_import_check="enabled"
50 | ; Checks for variables that could be declared immutable
51 | could_be_immutable_check="disabled"
52 | ; Checks for redundant expressions in if statements
53 | redundant_if_check="enabled"
54 | ; Checks for redundant parenthesis
55 | redundant_parens_check="enabled"
56 | ; Checks for mismatched argument and parameter names
57 | mismatched_args_check="enabled"
58 | ; Checks for labels with the same name as variables
59 | label_var_same_name_check="enabled"
60 | ; Checks for lines longer than 120 characters
61 | long_line_check="enabled"
62 | ; Checks for assignment to auto-ref function parameters
63 | auto_ref_assignment_check="enabled"
64 | ; Checks for incorrect infinite range definitions
65 | incorrect_infinite_range_check="enabled"
66 | ; Checks for asserts that are always true
67 | useless_assert_check="enabled"
68 | ; Check for uses of the old-style alias syntax
69 | alias_syntax_check="enabled"
70 | ; Checks for else if that should be else static if
71 | static_if_else_check="enabled"
72 | ; Check for unclear lambda syntax
73 | lambda_return_check="enabled"
74 | ; Check for auto function without return statement
75 | auto_function_check="enabled"
76 | ; Check for sortedness of imports
77 | imports_sortedness="disabled"
78 | ; Check for explicitly annotated unittests
79 | explicitly_annotated_unittests="disabled"
80 | ; Check for properly documented public functions (Returns, Params)
81 | properly_documented_public_functions="disabled"
82 | ; Check for useless usage of the final attribute
83 | final_attribute_check="enabled"
84 | ; Check for virtual calls in the class constructors
85 | vcall_in_ctor="enabled"
86 | ; Check for useless user defined initializers
87 | useless_initializer="disabled"
88 | ; Check allman brace style
89 | allman_braces_check="disabled"
90 | ; Check for redundant attributes
91 | redundant_attributes_check="enabled"
92 | ; Check public declarations without a documented unittest
93 | has_public_example="disabled"
94 | ; Check for asserts without an explanatory message
95 | assert_without_msg="disabled"
96 | ; Check indent of if constraints
97 | if_constraints_indent="disabled"
98 | ; Check for @trusted applied to a bigger scope than a single function
99 | trust_too_much="enabled"
100 | ; Check for redundant storage classes on variable declarations
101 | redundant_storage_classes="enabled"
102 | ; ModuleFilters for selectively enabling (+std) and disabling (-std.internal) i
103 | ; ndividual checks
104 | [analysis.config.ModuleFilters]
105 | ; Exclude/Import modules
106 | style_check=""
107 | ; Exclude/Import modules
108 | enum_array_literal_check=""
109 | ; Exclude/Import modules
110 | exception_check=""
111 | ; Exclude/Import modules
112 | delete_check=""
113 | ; Exclude/Import modules
114 | float_operator_check=""
115 | ; Exclude/Import modules
116 | number_style_check=""
117 | ; Exclude/Import modules
118 | object_const_check=""
119 | ; Exclude/Import modules
120 | backwards_range_check=""
121 | ; Exclude/Import modules
122 | if_else_same_check=""
123 | ; Exclude/Import modules
124 | constructor_check=""
125 | ; Exclude/Import modules
126 | unused_variable_check=""
127 | ; Exclude/Import modules
128 | unused_label_check=""
129 | ; Exclude/Import modules
130 | duplicate_attribute=""
131 | ; Exclude/Import modules
132 | opequals_tohash_check=""
133 | ; Exclude/Import modules
134 | length_subtraction_check=""
135 | ; Exclude/Import modules
136 | builtin_property_names_check=""
137 | ; Exclude/Import modules
138 | asm_style_check=""
139 | ; Exclude/Import modules
140 | logical_precedence_check=""
141 | ; Exclude/Import modules
142 | undocumented_declaration_check=""
143 | ; Exclude/Import modules
144 | function_attribute_check=""
145 | ; Exclude/Import modules
146 | comma_expression_check=""
147 | ; Exclude/Import modules
148 | local_import_check=""
149 | ; Exclude/Import modules
150 | could_be_immutable_check=""
151 | ; Exclude/Import modules
152 | redundant_if_check=""
153 | ; Exclude/Import modules
154 | redundant_parens_check=""
155 | ; Exclude/Import modules
156 | mismatched_args_check=""
157 | ; Exclude/Import modules
158 | label_var_same_name_check=""
159 | ; Exclude/Import modules
160 | long_line_check=""
161 | ; Exclude/Import modules
162 | auto_ref_assignment_check=""
163 | ; Exclude/Import modules
164 | incorrect_infinite_range_check=""
165 | ; Exclude/Import modules
166 | useless_assert_check=""
167 | ; Exclude/Import modules
168 | alias_syntax_check=""
169 | ; Exclude/Import modules
170 | static_if_else_check=""
171 | ; Exclude/Import modules
172 | lambda_return_check=""
173 | ; Exclude/Import modules
174 | auto_function_check=""
175 | ; Exclude/Import modules
176 | imports_sortedness=""
177 | ; Exclude/Import modules
178 | explicitly_annotated_unittests=""
179 | ; Exclude/Import modules
180 | properly_documented_public_functions=""
181 | ; Exclude/Import modules
182 | final_attribute_check=""
183 | ; Exclude/Import modules
184 | vcall_in_ctor=""
185 | ; Exclude/Import modules
186 | useless_initializer=""
187 | ; Exclude/Import modules
188 | allman_braces_check=""
189 | ; Exclude/Import modules
190 | redundant_attributes_check=""
191 | ; Exclude/Import modules
192 | has_public_example=""
193 | ; Exclude/Import modules
194 | assert_without_msg=""
195 | ; Exclude/Import modules
196 | if_constraints_indent=""
197 | ; Exclude/Import modules
198 | trust_too_much=""
199 | ; Exclude/Import modules
200 | redundant_storage_classes=""
201 |
--------------------------------------------------------------------------------
/tests/symbol/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "symbol"
3 | }
--------------------------------------------------------------------------------
/tests/symbol/messages/_order.txt:
--------------------------------------------------------------------------------
1 | initialize-symbolInformation
2 | initialized
3 | textDocument_documentSymbol-symbolInformation
4 | workspace_symbol
5 | shutdown
6 | initialize-documentSymbol
7 | initialized
8 | textDocument_documentSymbol-documentSymbol
9 | shutdown
10 |
--------------------------------------------------------------------------------
/tests/symbol/messages/initialize-documentSymbol.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "initialize",
4 | "method": "initialize",
5 | "params": {
6 | "processId": null,
7 | "rootUri": "testFile:///",
8 | "capabilities": {
9 | "workspace": {
10 | "symbol": {
11 | "dynamicRegistration": false
12 | }
13 | },
14 | "textDocument": {
15 | "documentSymbol": {
16 | "dynamicRegistration": false,
17 | "hierarchicalDocumentSymbolSupport": true
18 | }
19 | }
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/tests/symbol/messages/initialize-documentSymbol.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "initialize",
4 | "jsonrpc": "2.0",
5 | "result": {
6 | "capabilities": {
7 | "documentSymbolProvider": true,
8 | "textDocumentSync": {
9 | "change": 2,
10 | "openClose": true,
11 | "save": {
12 | "includeText": false
13 | },
14 | "willSave": true,
15 | "willSaveWaitUntil": false
16 | },
17 | "workspaceSymbolProvider": true
18 | }
19 | }
20 | }
21 | ]
--------------------------------------------------------------------------------
/tests/symbol/messages/initialize-symbolInformation.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "initialize",
4 | "method": "initialize",
5 | "params": {
6 | "processId": null,
7 | "rootUri": "testFile:///",
8 | "capabilities": {
9 | "workspace": {
10 | "symbol": {
11 | "dynamicRegistration": false
12 | }
13 | },
14 | "textDocument": {
15 | "documentSymbol": {
16 | "dynamicRegistration": false
17 | }
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/tests/symbol/messages/initialize-symbolInformation.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "initialize",
4 | "jsonrpc": "2.0",
5 | "result": {
6 | "capabilities": {
7 | "documentSymbolProvider": true,
8 | "textDocumentSync": {
9 | "change": 2,
10 | "openClose": true,
11 | "save": {
12 | "includeText": false
13 | },
14 | "willSave": true,
15 | "willSaveWaitUntil": false
16 | },
17 | "workspaceSymbolProvider": true
18 | }
19 | }
20 | }
21 | ]
--------------------------------------------------------------------------------
/tests/symbol/messages/initialized.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "method": "initialized",
4 | "params": {}
5 | }
--------------------------------------------------------------------------------
/tests/symbol/messages/initialized.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "jsonrpc": "2.0",
4 | "method": "textDocument/publishDiagnostics",
5 | "params": {
6 | "diagnostics": [],
7 | "uri": "testFile:///source/lib.d"
8 | }
9 | },
10 | {
11 | "jsonrpc": "2.0",
12 | "method": "textDocument/publishDiagnostics",
13 | "params": {
14 | "diagnostics": [],
15 | "uri": "testFile:///source/app.d"
16 | }
17 | }
18 | ]
--------------------------------------------------------------------------------
/tests/symbol/messages/shutdown.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "shutdown",
4 | "method": "shutdown",
5 | "params": {}
6 | }
--------------------------------------------------------------------------------
/tests/symbol/messages/shutdown.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "shutdown",
4 | "jsonrpc": "2.0",
5 | "result": null
6 | }
7 | ]
--------------------------------------------------------------------------------
/tests/symbol/messages/textDocument_documentSymbol-documentSymbol.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/documentSymbol",
4 | "method": "textDocument/documentSymbol",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/symbol/messages/textDocument_documentSymbol-symbolInformation.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "textDocument/documentSymbol",
4 | "method": "textDocument/documentSymbol",
5 | "params": {
6 | "textDocument": {
7 | "uri": "testFile:///source/app.d"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/symbol/messages/textDocument_documentSymbol-symbolInformation.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "textDocument/documentSymbol",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "containerName": "",
8 | "kind": 12,
9 | "location": {
10 | "range": {
11 | "end": {
12 | "character": 9,
13 | "line": 2
14 | },
15 | "start": {
16 | "character": 5,
17 | "line": 2
18 | }
19 | },
20 | "uri": "testFile:///source/app.d"
21 | },
22 | "name": "main"
23 | },
24 | {
25 | "containerName": "",
26 | "kind": 5,
27 | "location": {
28 | "range": {
29 | "end": {
30 | "character": 11,
31 | "line": 8
32 | },
33 | "start": {
34 | "character": 6,
35 | "line": 8
36 | }
37 | },
38 | "uri": "testFile:///source/app.d"
39 | },
40 | "name": "Class"
41 | },
42 | {
43 | "containerName": "Class",
44 | "kind": 13,
45 | "location": {
46 | "range": {
47 | "end": {
48 | "character": 14,
49 | "line": 10
50 | },
51 | "start": {
52 | "character": 8,
53 | "line": 10
54 | }
55 | },
56 | "uri": "testFile:///source/app.d"
57 | },
58 | "name": "number"
59 | },
60 | {
61 | "containerName": "Class",
62 | "kind": 11,
63 | "location": {
64 | "range": {
65 | "end": {
66 | "character": 9,
67 | "line": 12
68 | },
69 | "start": {
70 | "character": 4,
71 | "line": 12
72 | }
73 | },
74 | "uri": "testFile:///source/app.d"
75 | },
76 | "name": ""
77 | },
78 | {
79 | "containerName": "",
80 | "kind": 13,
81 | "location": {
82 | "range": {
83 | "end": {
84 | "character": 21,
85 | "line": 14
86 | },
87 | "start": {
88 | "character": 12,
89 | "line": 14
90 | }
91 | },
92 | "uri": "testFile:///source/app.d"
93 | },
94 | "name": "anonymous"
95 | },
96 | {
97 | "containerName": "",
98 | "kind": 13,
99 | "location": {
100 | "range": {
101 | "end": {
102 | "character": 21,
103 | "line": 15
104 | },
105 | "start": {
106 | "character": 15,
107 | "line": 15
108 | }
109 | },
110 | "uri": "testFile:///source/app.d"
111 | },
112 | "name": "symbol"
113 | },
114 | {
115 | "containerName": "",
116 | "kind": 23,
117 | "location": {
118 | "range": {
119 | "end": {
120 | "character": 13,
121 | "line": 19
122 | },
123 | "start": {
124 | "character": 7,
125 | "line": 19
126 | }
127 | },
128 | "uri": "testFile:///source/app.d"
129 | },
130 | "name": "Struct"
131 | },
132 | {
133 | "containerName": "Struct",
134 | "kind": 13,
135 | "location": {
136 | "range": {
137 | "end": {
138 | "character": 15,
139 | "line": 21
140 | },
141 | "start": {
142 | "character": 11,
143 | "line": 21
144 | }
145 | },
146 | "uri": "testFile:///source/app.d"
147 | },
148 | "name": "name"
149 | },
150 | {
151 | "containerName": "",
152 | "kind": 10,
153 | "location": {
154 | "range": {
155 | "end": {
156 | "character": 9,
157 | "line": 24
158 | },
159 | "start": {
160 | "character": 5,
161 | "line": 24
162 | }
163 | },
164 | "uri": "testFile:///source/app.d"
165 | },
166 | "name": "Enum"
167 | },
168 | {
169 | "containerName": "Enum",
170 | "kind": 22,
171 | "location": {
172 | "range": {
173 | "end": {
174 | "character": 7,
175 | "line": 26
176 | },
177 | "start": {
178 | "character": 4,
179 | "line": 26
180 | }
181 | },
182 | "uri": "testFile:///source/app.d"
183 | },
184 | "name": "foo"
185 | },
186 | {
187 | "containerName": "Enum",
188 | "kind": 22,
189 | "location": {
190 | "range": {
191 | "end": {
192 | "character": 7,
193 | "line": 27
194 | },
195 | "start": {
196 | "character": 4,
197 | "line": 27
198 | }
199 | },
200 | "uri": "testFile:///source/app.d"
201 | },
202 | "name": "bar"
203 | },
204 | {
205 | "containerName": "Enum",
206 | "kind": 22,
207 | "location": {
208 | "range": {
209 | "end": {
210 | "character": 7,
211 | "line": 28
212 | },
213 | "start": {
214 | "character": 4,
215 | "line": 28
216 | }
217 | },
218 | "uri": "testFile:///source/app.d"
219 | },
220 | "name": "baz"
221 | }
222 | ]
223 | }
224 | ]
--------------------------------------------------------------------------------
/tests/symbol/messages/workspace_symbol.in.json:
--------------------------------------------------------------------------------
1 | {
2 | "jsonrpc": "2.0",
3 | "id": "workspace/symbol",
4 | "method": "workspace/symbol",
5 | "params": {
6 | "query": ""
7 | }
8 | }
--------------------------------------------------------------------------------
/tests/symbol/messages/workspace_symbol.ref.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "workspace/symbol",
4 | "jsonrpc": "2.0",
5 | "result": [
6 | {
7 | "containerName": "",
8 | "kind": 12,
9 | "location": {
10 | "range": {
11 | "end": {
12 | "character": 9,
13 | "line": 2
14 | },
15 | "start": {
16 | "character": 5,
17 | "line": 2
18 | }
19 | },
20 | "uri": "testFile:///source/app.d"
21 | },
22 | "name": "main"
23 | },
24 | {
25 | "containerName": "",
26 | "kind": 5,
27 | "location": {
28 | "range": {
29 | "end": {
30 | "character": 11,
31 | "line": 8
32 | },
33 | "start": {
34 | "character": 6,
35 | "line": 8
36 | }
37 | },
38 | "uri": "testFile:///source/app.d"
39 | },
40 | "name": "Class"
41 | },
42 | {
43 | "containerName": "Class",
44 | "kind": 13,
45 | "location": {
46 | "range": {
47 | "end": {
48 | "character": 14,
49 | "line": 10
50 | },
51 | "start": {
52 | "character": 8,
53 | "line": 10
54 | }
55 | },
56 | "uri": "testFile:///source/app.d"
57 | },
58 | "name": "number"
59 | },
60 | {
61 | "containerName": "Class",
62 | "kind": 13,
63 | "location": {
64 | "range": {
65 | "end": {
66 | "character": 21,
67 | "line": 14
68 | },
69 | "start": {
70 | "character": 12,
71 | "line": 14
72 | }
73 | },
74 | "uri": "testFile:///source/app.d"
75 | },
76 | "name": "anonymous"
77 | },
78 | {
79 | "containerName": "Class",
80 | "kind": 13,
81 | "location": {
82 | "range": {
83 | "end": {
84 | "character": 21,
85 | "line": 15
86 | },
87 | "start": {
88 | "character": 15,
89 | "line": 15
90 | }
91 | },
92 | "uri": "testFile:///source/app.d"
93 | },
94 | "name": "symbol"
95 | },
96 | {
97 | "containerName": "",
98 | "kind": 23,
99 | "location": {
100 | "range": {
101 | "end": {
102 | "character": 13,
103 | "line": 19
104 | },
105 | "start": {
106 | "character": 7,
107 | "line": 19
108 | }
109 | },
110 | "uri": "testFile:///source/app.d"
111 | },
112 | "name": "Struct"
113 | },
114 | {
115 | "containerName": "Struct",
116 | "kind": 12,
117 | "location": {
118 | "range": {
119 | "end": {
120 | "character": 13,
121 | "line": 19
122 | },
123 | "start": {
124 | "character": 7,
125 | "line": 19
126 | }
127 | },
128 | "uri": "testFile:///source/app.d"
129 | },
130 | "name": "this"
131 | },
132 | {
133 | "containerName": "Struct",
134 | "kind": 13,
135 | "location": {
136 | "range": {
137 | "end": {
138 | "character": 15,
139 | "line": 21
140 | },
141 | "start": {
142 | "character": 11,
143 | "line": 21
144 | }
145 | },
146 | "uri": "testFile:///source/app.d"
147 | },
148 | "name": "name"
149 | },
150 | {
151 | "containerName": "",
152 | "kind": 10,
153 | "location": {
154 | "range": {
155 | "end": {
156 | "character": 9,
157 | "line": 24
158 | },
159 | "start": {
160 | "character": 5,
161 | "line": 24
162 | }
163 | },
164 | "uri": "testFile:///source/app.d"
165 | },
166 | "name": "Enum"
167 | },
168 | {
169 | "containerName": "Enum",
170 | "kind": 22,
171 | "location": {
172 | "range": {
173 | "end": {
174 | "character": 7,
175 | "line": 26
176 | },
177 | "start": {
178 | "character": 4,
179 | "line": 26
180 | }
181 | },
182 | "uri": "testFile:///source/app.d"
183 | },
184 | "name": "foo"
185 | },
186 | {
187 | "containerName": "Enum",
188 | "kind": 22,
189 | "location": {
190 | "range": {
191 | "end": {
192 | "character": 7,
193 | "line": 27
194 | },
195 | "start": {
196 | "character": 4,
197 | "line": 27
198 | }
199 | },
200 | "uri": "testFile:///source/app.d"
201 | },
202 | "name": "bar"
203 | },
204 | {
205 | "containerName": "Enum",
206 | "kind": 22,
207 | "location": {
208 | "range": {
209 | "end": {
210 | "character": 7,
211 | "line": 28
212 | },
213 | "start": {
214 | "character": 4,
215 | "line": 28
216 | }
217 | },
218 | "uri": "testFile:///source/app.d"
219 | },
220 | "name": "baz"
221 | },
222 | {
223 | "containerName": "",
224 | "kind": 12,
225 | "location": {
226 | "range": {
227 | "end": {
228 | "character": 17,
229 | "line": 0
230 | },
231 | "start": {
232 | "character": 5,
233 | "line": 0
234 | }
235 | },
236 | "uri": "testFile:///source/lib.d"
237 | },
238 | "name": "someFunction"
239 | },
240 | {
241 | "containerName": "",
242 | "kind": 11,
243 | "location": {
244 | "range": {
245 | "end": {
246 | "character": 23,
247 | "line": 5
248 | },
249 | "start": {
250 | "character": 10,
251 | "line": 5
252 | }
253 | },
254 | "uri": "testFile:///source/lib.d"
255 | },
256 | "name": "SomeInterface"
257 | }
258 | ]
259 | }
260 | ]
--------------------------------------------------------------------------------
/tests/symbol/source/app.d:
--------------------------------------------------------------------------------
1 | import std.stdio;
2 |
3 | void main()
4 | {
5 | auto var = 42;
6 | writeln("Edit source/app.d to start your project.");
7 | }
8 |
9 | class Class
10 | {
11 | int number;
12 |
13 | union
14 | {
15 | int anonymous;
16 | string symbol;
17 | }
18 | }
19 |
20 | struct Struct
21 | {
22 | string name;
23 | }
24 |
25 | enum Enum
26 | {
27 | foo,
28 | bar,
29 | baz
30 | }
31 |
--------------------------------------------------------------------------------
/tests/symbol/source/lib.d:
--------------------------------------------------------------------------------
1 | void someFunction()
2 | {
3 | //...
4 | }
5 |
6 | interface SomeInterface
7 | {
8 | //...
9 | }
10 |
--------------------------------------------------------------------------------
/util/dub.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "util",
3 | "authors": [
4 | "Laurent Tréguier"
5 | ],
6 | "description": "DLS utilities",
7 | "copyright": "Copyright © 2018, Laurent Tréguier",
8 | "license": "GPL-3.0 or later",
9 | "dependencies": {
10 | "dls:protocol": "*"
11 | }
12 | }
--------------------------------------------------------------------------------
/util/source/dls/util/communicator.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.util.communicator;
22 |
23 | import std.stdio : File;
24 |
25 | // Socket can't be used with a shared aliasing.
26 | // However, the communicator's methods are all called either from the main
27 | // thread, or from inside a synchronized block, so __gshared is ok.
28 | private __gshared Communicator _communicator;
29 | private __gshared File _stdin;
30 | private __gshared File _stdout;
31 |
32 | shared static this()
33 | {
34 | import std.stdio : stdin, stdout;
35 |
36 | _stdin = stdin;
37 | _stdout = stdout;
38 |
39 | version (Windows)
40 | {
41 | stdin = File("NUL", "rb");
42 | stdout = File("NUL", "wb");
43 | }
44 | else version (Posix)
45 | {
46 | stdin = File("/dev/null", "rb");
47 | stdout = File("/dev/null", "wb");
48 | }
49 | }
50 |
51 | shared static ~this()
52 | {
53 | if (_communicator !is null)
54 | {
55 | destroy(communicator);
56 | }
57 | }
58 |
59 | @property Communicator communicator()
60 | {
61 | return _communicator;
62 | }
63 |
64 | @property void communicator(Communicator c)
65 | {
66 | assert(_communicator is null);
67 | _communicator = c;
68 | }
69 |
70 | interface Communicator
71 | {
72 | bool hasData();
73 | bool hasPendingData();
74 | char[] read(const size_t size);
75 | void write(const char[] data);
76 | void flush();
77 | }
78 |
79 | class StdioCommunicator : Communicator
80 | {
81 | import std.parallelism : Task, TaskPool;
82 |
83 | private bool _checkPending;
84 | private TaskPool _pool;
85 | private Task!(readChar)* _background;
86 |
87 | static char readChar()
88 | {
89 | static char[1] buffer;
90 | auto result = _stdin.rawRead(buffer);
91 |
92 | if (result.length > 0)
93 | {
94 | return result[0];
95 | }
96 |
97 | throw new Exception("No input data");
98 | }
99 |
100 | this(bool checkPendingData)
101 | {
102 | _checkPending = checkPendingData;
103 |
104 | if (checkPendingData)
105 | {
106 | _pool = new TaskPool(1);
107 | _pool.isDaemon = true;
108 | startBackground();
109 | }
110 | }
111 |
112 | ~this()
113 | {
114 | if (_checkPending)
115 | {
116 | _pool.stop();
117 | }
118 | }
119 |
120 | bool hasData()
121 | {
122 | return _stdin.isOpen && !_stdin.eof;
123 | }
124 |
125 | bool hasPendingData()
126 | {
127 | try
128 | {
129 | return _checkPending && _background.done;
130 | }
131 | catch (Exception e)
132 | {
133 | return false;
134 | }
135 | }
136 |
137 | char[] read(const size_t size)
138 | {
139 | if (size == 0)
140 | {
141 | return [];
142 | }
143 |
144 | static char[] buffer;
145 | buffer.length = size;
146 |
147 | if (!_checkPending)
148 | {
149 | return _stdin.rawRead(buffer);
150 | }
151 |
152 | scope(exit)
153 | {
154 | startBackground();
155 | }
156 |
157 | try
158 | {
159 | buffer[0] = _background.yieldForce();
160 | }
161 | catch (Exception e)
162 | {
163 | return hasData() ? _stdin.rawRead(buffer) : [];
164 | }
165 |
166 | if (size > 1)
167 | {
168 | buffer = buffer[0 .. _stdin.rawRead(buffer[1 .. $]).length + 1];
169 | }
170 |
171 | return buffer;
172 | }
173 |
174 | void write(const char[] data)
175 | {
176 | _stdout.rawWrite(data);
177 | }
178 |
179 | void flush()
180 | {
181 | _stdout.flush();
182 | }
183 |
184 | private void startBackground()
185 | {
186 | import std.parallelism : task;
187 |
188 | if (_checkPending && hasData())
189 | {
190 | _background = task!readChar;
191 | _pool.put(_background);
192 | }
193 | }
194 | }
195 |
196 | class SocketCommunicator : Communicator
197 | {
198 | import std.socket : Socket;
199 |
200 | private Socket _socket;
201 |
202 | this(ushort port)
203 | {
204 | import std.socket : AddressInfo, InternetAddress, SocketOption,
205 | SocketOptionLevel, TcpSocket;
206 |
207 | _socket = new TcpSocket(new InternetAddress("localhost", port));
208 | _socket.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, 1);
209 | }
210 |
211 | bool hasData()
212 | {
213 | synchronized (_socket)
214 | {
215 | return _socket.isAlive;
216 | }
217 | }
218 |
219 | bool hasPendingData()
220 | {
221 | import std.socket : SocketFlags;
222 |
223 | static char[1] buffer;
224 | ptrdiff_t result;
225 |
226 | synchronized (_socket)
227 | {
228 | _socket.blocking = false;
229 | result = _socket.receive(buffer, SocketFlags.PEEK);
230 | _socket.blocking = true;
231 | }
232 |
233 | return result != Socket.ERROR && result > 0;
234 | }
235 |
236 | char[] read(const size_t size)
237 | {
238 | static char[] buffer;
239 | buffer.length = size;
240 | ptrdiff_t totalBytesReceived;
241 | ptrdiff_t bytesReceived;
242 |
243 | do
244 | {
245 | synchronized (_socket)
246 | {
247 | bytesReceived = _socket.receive(buffer);
248 | }
249 |
250 | if (bytesReceived != Socket.ERROR)
251 | {
252 | totalBytesReceived += bytesReceived;
253 | }
254 | else if (bytesReceived == 0)
255 | {
256 | buffer.length = totalBytesReceived;
257 | break;
258 | }
259 | }
260 | while (bytesReceived == Socket.ERROR || totalBytesReceived < size);
261 |
262 | return buffer;
263 | }
264 |
265 | void write(const char[] data)
266 | {
267 | ptrdiff_t totalBytesSent;
268 | ptrdiff_t bytesSent;
269 |
270 | do
271 | {
272 | synchronized (_socket)
273 | {
274 | bytesSent = _socket.send(data[totalBytesSent .. $]);
275 | }
276 |
277 | if (bytesSent != Socket.ERROR)
278 | {
279 | totalBytesSent += bytesSent;
280 | }
281 | }
282 | while (bytesSent == Socket.ERROR || totalBytesSent < data.length);
283 | }
284 |
285 | void flush()
286 | {
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/util/source/dls/util/disposable_fiber.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.util.disposable_fiber;
22 |
23 | import core.thread : Fiber;
24 |
25 | class FiberDisposedException : Exception
26 | {
27 | this()
28 | {
29 | super("Fiber disposed");
30 | }
31 | }
32 |
33 | class DisposableFiber : Fiber
34 | {
35 | static bool safeMode;
36 | private bool _disposed;
37 |
38 | static DisposableFiber getThis()
39 | {
40 | const thisFiber = Fiber.getThis();
41 | assert(typeid(thisFiber) == typeid(DisposableFiber));
42 | return cast(DisposableFiber) thisFiber;
43 | }
44 |
45 | static void yield()
46 | {
47 | if (safeMode)
48 | {
49 | return;
50 | }
51 |
52 | Fiber.yield();
53 |
54 | if (getThis()._disposed)
55 | {
56 | throw new FiberDisposedException();
57 | }
58 | }
59 |
60 | this(void delegate() dg)
61 | {
62 | size_t pageSize;
63 |
64 | version (Windows)
65 | {
66 | import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
67 |
68 | SYSTEM_INFO info;
69 | GetSystemInfo(&info);
70 | pageSize = cast(size_t) info.dwPageSize;
71 | }
72 | else version (Posix)
73 | {
74 | import core.sys.posix.unistd : _SC_PAGESIZE, sysconf;
75 |
76 | pageSize = cast(size_t) sysconf(_SC_PAGESIZE);
77 | }
78 | else
79 | {
80 | pageSize = 4096;
81 | }
82 |
83 | super(dg, pageSize * 32);
84 | }
85 |
86 | void dispose()
87 | {
88 | _disposed = true;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/util/source/dls/util/document.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.util.document;
22 |
23 | class Document
24 | {
25 | import dls.util.uri : Uri;
26 | import dls.protocol.definitions : DocumentUri, Position, Range,
27 | TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier;
28 | import dls.protocol.interfaces : TextDocumentContentChangeEvent;
29 | import std.json : JSONValue;
30 |
31 | private static Document[string] _documents;
32 | private DocumentUri _uri;
33 | private wstring[] _lines;
34 | private JSONValue _version;
35 |
36 | @property static auto uris()
37 | {
38 | import std.algorithm : map;
39 |
40 | return _documents.byValue.map!(doc => new Uri(doc._uri));
41 | }
42 |
43 | static Document get(const Uri uri)
44 | {
45 | import std.file : readText;
46 |
47 | return uri.path in _documents ? _documents[uri.path] : new Document(uri, readText(uri.path));
48 | }
49 |
50 | static bool open(const TextDocumentItem textDocument)
51 | {
52 | auto uri = new Uri(textDocument.uri);
53 |
54 | if (uri.path !in _documents)
55 | {
56 | _documents[uri.path] = new Document(uri, textDocument.text);
57 | _documents[uri.path]._version = textDocument.version_;
58 | return true;
59 | }
60 | else
61 | {
62 | return false;
63 | }
64 | }
65 |
66 | static bool close(const TextDocumentIdentifier textDocument)
67 | {
68 | auto uri = new Uri(textDocument.uri);
69 |
70 | if (uri.path in _documents)
71 | {
72 | _documents.remove(uri.path);
73 | return true;
74 | }
75 | else
76 | {
77 | return false;
78 | }
79 | }
80 |
81 | static bool change(const VersionedTextDocumentIdentifier textDocument,
82 | TextDocumentContentChangeEvent[] events)
83 | {
84 | auto uri = new Uri(textDocument.uri);
85 |
86 | if (uri.path in _documents)
87 | {
88 | _documents[uri.path].change(events);
89 | _documents[uri.path]._version = textDocument.version_;
90 | return true;
91 | }
92 | else
93 | {
94 | return false;
95 | }
96 | }
97 |
98 | @property const(wstring[]) lines() const
99 | {
100 | return _lines;
101 | }
102 |
103 | @property JSONValue version_() const
104 | {
105 | return _version;
106 | }
107 |
108 | private this(const Uri uri, const string text)
109 | {
110 | _uri = uri;
111 | _lines = getText(text);
112 | }
113 |
114 | override string toString() const
115 | {
116 | import std.range : join;
117 | import std.utf : toUTF8;
118 |
119 | return _lines.join().toUTF8();
120 | }
121 |
122 | void validatePosition(const Position position) const
123 | {
124 | import dls.protocol.errors : InvalidParamsException;
125 | import std.format : format;
126 |
127 | if (position.line >= _lines.length || position.character > _lines[position.line].length)
128 | {
129 | throw new InvalidParamsException(format!"invalid position: %s %s,%s"(_uri,
130 | position.line, position.character));
131 | }
132 | }
133 |
134 | size_t byteAtPosition(const Position position) const
135 | {
136 | import std.algorithm : min;
137 | import std.utf : codeLength;
138 |
139 | size_t result;
140 |
141 | foreach (i, ref line; _lines)
142 | {
143 | if (i < position.line)
144 | {
145 | result += codeLength!char(line);
146 | }
147 | else
148 | {
149 | result += codeLength!char(line[0 .. min(position.character, $)]);
150 | break;
151 | }
152 | }
153 |
154 | return result;
155 | }
156 |
157 | Position positionAtByte(size_t bytePosition) const
158 | {
159 | import std.algorithm : min;
160 | import std.utf : codeLength, toUTF8;
161 |
162 | size_t i;
163 | size_t bytes;
164 |
165 | while (bytes <= bytePosition && i < _lines.length)
166 | {
167 | bytes += codeLength!char(_lines[i]);
168 | ++i;
169 | }
170 |
171 | immutable lineNumber = minusOne(i);
172 | immutable line = _lines[lineNumber];
173 | bytes -= codeLength!char(line);
174 | immutable columnByte = min(bytePosition - bytes, line.length);
175 | immutable columnNumber = codeLength!wchar(line.toUTF8()[0 .. columnByte]);
176 | return new Position(lineNumber, columnNumber);
177 | }
178 |
179 | Range wordRangeAtPosition(const Position position) const
180 | {
181 | import std.algorithm : min;
182 |
183 | immutable line = _lines[min(position.line, $ - 1)];
184 | immutable middleIndex = min(position.character, line.length);
185 | size_t startIndex = middleIndex;
186 | size_t endIndex = middleIndex;
187 |
188 | static bool isIdentifierChar(wchar c)
189 | {
190 | import std.ascii : isPunctuation, isWhite;
191 |
192 | return !isWhite(c) && (!isPunctuation(c) || c == '_');
193 | }
194 |
195 | while (startIndex > 0 && isIdentifierChar(line[minusOne(startIndex)]))
196 | {
197 | --startIndex;
198 | }
199 |
200 | while (endIndex < line.length && isIdentifierChar(line[endIndex]))
201 | {
202 | ++endIndex;
203 | }
204 |
205 | return new Range(new Position(position.line, startIndex),
206 | new Position(position.line, endIndex));
207 | }
208 |
209 | Range wordRangeAtLineAndByte(size_t lineNumber, size_t bytePosition) const
210 | {
211 | import std.algorithm : min;
212 | import std.utf : codeLength, toUTF8;
213 |
214 | return wordRangeAtPosition(new Position(lineNumber,
215 | codeLength!wchar(_lines[lineNumber].toUTF8()[0 .. min(bytePosition, $)])));
216 | }
217 |
218 | Range wordRangeAtByte(size_t bytePosition) const
219 | {
220 | return wordRangeAtPosition(positionAtByte(bytePosition));
221 | }
222 |
223 | private void change(const TextDocumentContentChangeEvent[] events)
224 | {
225 | foreach (event; events)
226 | {
227 | if (event.range.isNull)
228 | {
229 | _lines = getText(event.text);
230 | }
231 | else
232 | {
233 | with (event.range)
234 | {
235 | auto linesBefore = _lines[0 .. start.line];
236 | auto linesAfter = _lines[end.line + 1 .. $];
237 |
238 | auto lineStart = _lines[start.line][0 .. start.character];
239 | auto lineEnd = _lines[end.line][end.character .. $];
240 |
241 | auto newLines = getText(event.text);
242 |
243 | if (newLines.length)
244 | {
245 | newLines[0] = lineStart ~ newLines[0];
246 | newLines[$ - 1] = newLines[$ - 1] ~ lineEnd;
247 | }
248 | else
249 | {
250 | newLines = [lineStart ~ lineEnd];
251 | }
252 |
253 | _lines = linesBefore ~ newLines ~ linesAfter;
254 | }
255 | }
256 | }
257 | }
258 |
259 | private wstring[] getText(const string text) const
260 | {
261 | import std.algorithm : endsWith;
262 | import std.array : replaceFirst;
263 | import std.encoding : getBOM;
264 | import std.string : splitLines;
265 | import std.typecons : Yes;
266 | import std.utf : toUTF16;
267 |
268 | auto lines = text.replaceFirst(cast(string) getBOM(cast(ubyte[]) text)
269 | .sequence, "").toUTF16().splitLines(Yes.keepTerminator);
270 |
271 | if (!lines.length || lines[$ - 1].endsWith('\r', '\n'))
272 | {
273 | lines ~= "";
274 | }
275 |
276 | return lines;
277 | }
278 | }
279 |
280 | size_t minusOne(size_t i)
281 | {
282 | return i > 0 ? i - 1 : 0;
283 | }
284 |
--------------------------------------------------------------------------------
/util/source/dls/util/uri.d:
--------------------------------------------------------------------------------
1 | /*
2 | *Copyright (C) 2018 Laurent Tréguier
3 | *
4 | *This file is part of DLS.
5 | *
6 | *DLS is free software: you can redistribute it and/or modify
7 | *it under the terms of the GNU General Public License as published by
8 | *the Free Software Foundation, either version 3 of the License, or
9 | *(at your option) any later version.
10 | *
11 | *DLS is distributed in the hope that it will be useful,
12 | *but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | *GNU General Public License for more details.
15 | *
16 | *You should have received a copy of the GNU General Public License
17 | *along with DLS. If not, see .
18 | *
19 | */
20 |
21 | module dls.util.uri;
22 |
23 | class Uri
24 | {
25 | import dls.protocol.definitions : DocumentUri;
26 | import std.regex : regex;
27 |
28 | private static enum _reg = regex(`([\w-]+):(?://([\w.@]+(?::\d+)?))?([^\?#]+)(?:\?([\w=&]+))?(?:#([\w-]+))?`);
29 | private string _uri;
30 | private string _scheme;
31 | private string _authority;
32 | private string _path;
33 | private string _query;
34 | private string _fragment;
35 |
36 | @property string path() const
37 | {
38 | return _path;
39 | }
40 |
41 | this(DocumentUri uri)
42 | {
43 | import std.regex : matchAll;
44 | import std.uri : decodeComponent;
45 |
46 | _uri = decodeComponent(uri);
47 | auto matches = matchAll(_uri, _reg);
48 |
49 | //dfmt off
50 | _scheme = matches.front[1];
51 | _authority = matches.front[2];
52 | _path = matches.front[3].normalized;
53 | _query = matches.front[4];
54 | _fragment = matches.front[5];
55 | //dfmt on
56 | }
57 |
58 | private this(string scheme, string authority, string path, string query, string fragment)
59 | {
60 | _uri = scheme ~ ':';
61 |
62 | if (authority !is null)
63 | {
64 | _uri ~= "//" ~ authority;
65 | }
66 |
67 | _uri ~= path;
68 |
69 | if (query !is null)
70 | {
71 | _uri ~= '?' ~ query;
72 | }
73 |
74 | if (fragment !is null)
75 | {
76 | _uri ~= '#' ~ fragment;
77 | }
78 |
79 | //dfmt off
80 | _scheme = scheme;
81 | _authority = authority;
82 | _path = path.normalized;
83 | _query = query;
84 | _fragment = fragment;
85 | //dfmt on
86 | }
87 |
88 | override string toString() const
89 | {
90 | return _uri;
91 | }
92 |
93 | static Uri fromPath(string path)
94 | {
95 | import std.algorithm : startsWith;
96 | import std.format : format;
97 | import std.string : tr;
98 | import std.uri : encode;
99 |
100 | immutable uriPath = path.tr(`\`, `/`);
101 | return new Uri("file", "", (uriPath.startsWith('/') ? "" : "/") ~ uriPath, null, null);
102 | }
103 |
104 | alias toString this;
105 | }
106 |
107 | string normalized(const string path)
108 | {
109 | import std.array : array;
110 | import std.path : asNormalizedPath;
111 |
112 | string res;
113 |
114 | version (Windows)
115 | {
116 | import std.algorithm : startsWith;
117 | import std.path : driveName, stripDrive;
118 | import std.uni : asUpperCase;
119 | import std.utf : toUTF8;
120 |
121 | if (path.startsWith('/') || path.startsWith('\\'))
122 | {
123 | return path[1 .. $].normalized;
124 | }
125 |
126 | res = driveName(path).asUpperCase().toUTF8() ~ stripDrive(path);
127 | }
128 | else
129 | {
130 | res = path;
131 | }
132 |
133 | return asNormalizedPath(res).array;
134 | }
135 |
136 | int filenameCmp(const Uri a, const Uri b)
137 | {
138 | import std.path : filenameCmp;
139 |
140 | return filenameCmp(a.path, b.path);
141 | }
142 |
143 | bool sameFile(const Uri a, const Uri b)
144 | {
145 | return filenameCmp(a, b) == 0;
146 | }
147 |
--------------------------------------------------------------------------------