Initial commit
This commit is contained in:
commit
474bd3c2f7
|
@ -0,0 +1,18 @@
|
|||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"name": "Launch Client",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceRoot}"],
|
||||
"outFiles": ["${workspaceRoot}/client/out/**/*.js"],
|
||||
"preLaunchTask": {
|
||||
"type": "npm",
|
||||
"script": "watch"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "compile",
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"reveal": "never"
|
||||
},
|
||||
"problemMatcher": ["$tsc"]
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"isBackground": true,
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"reveal": "never"
|
||||
},
|
||||
"problemMatcher": ["$tsc-watch"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
const vscode = require("vscode");
|
||||
const languageClient = require("vscode-languageclient");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
// Defines the search path of your language server DLL. (.NET Core)
|
||||
const languageServerPaths = [
|
||||
"server/DemoLanguageServer.dll",
|
||||
"../UpsilonLanguageServer/UpsilonLanguageServer/bin/Debug/netcoreapp2.1/UpsilonLanguageServer.dll"
|
||||
];
|
||||
function activateLanguageServer(context) {
|
||||
// The server is implemented in an executable application.
|
||||
let serverModule = null;
|
||||
for (let p of languageServerPaths) {
|
||||
p = context.asAbsolutePath(p);
|
||||
// console.log(p);
|
||||
if (fs.existsSync(p)) {
|
||||
serverModule = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!serverModule)
|
||||
throw new URIError("Cannot find the language server module.");
|
||||
let workPath = path.dirname(serverModule);
|
||||
console.log(`Use ${serverModule} as server module.`);
|
||||
console.log(`Work path: ${workPath}.`);
|
||||
// If the extension is launched in debug mode then the debug server options are used
|
||||
// Otherwise the run options are used
|
||||
let serverOptions = {
|
||||
run: {
|
||||
command: "dotnet",
|
||||
args: [serverModule],
|
||||
options: { cwd: workPath }
|
||||
},
|
||||
debug: {
|
||||
command: "dotnet",
|
||||
args: [serverModule, "--debug"],
|
||||
options: { cwd: workPath }
|
||||
}
|
||||
};
|
||||
// Options to control the language client
|
||||
let clientOptions = {
|
||||
// Register the server for plain text documents
|
||||
documentSelector: [
|
||||
{
|
||||
language: "upsilon",
|
||||
scheme: "file"
|
||||
}
|
||||
],
|
||||
synchronize: {
|
||||
// Synchronize the setting section 'languageServerExample' to the server
|
||||
configurationSection: "upsilonLanguageServer",
|
||||
// Notify the server about file changes to '.clientrc files contain in the workspace
|
||||
fileEvents: [
|
||||
vscode.workspace.createFileSystemWatcher("**/.clientrc"),
|
||||
vscode.workspace.createFileSystemWatcher("**/.yup"),
|
||||
vscode.workspace.createFileSystemWatcher("**/.lua")
|
||||
]
|
||||
}
|
||||
};
|
||||
// Create the language client and start the client.
|
||||
let client = new languageClient.LanguageClient("upsilonLanguageServer", "Upsilon Language Server", serverOptions, clientOptions);
|
||||
let disposable = client.start();
|
||||
// Push the disposable to the context's subscriptions so that the
|
||||
// client can be deactivated on extension deactivation
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
||||
// this method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
function activate(context) {
|
||||
console.log("Upsilon extension is now activated.");
|
||||
activateLanguageServer(context);
|
||||
}
|
||||
exports.activate = activate;
|
||||
// this method is called when your extension is deactivated
|
||||
function deactivate() { }
|
||||
exports.deactivate = deactivate;
|
||||
6;
|
||||
//# sourceMappingURL=extension.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AACb,6DAA6D;AAC7D,8EAA8E;AAE9E,iCAAiC;AACjC,wDAAwD;AACxD,6BAA6B;AAC7B,yBAAyB;AAEzB,mEAAmE;AACnE,MAAM,mBAAmB,GAAG;IAC1B,+BAA+B;IAC/B,kGAAkG;CACnG,CAAC;AAEF,SAAS,sBAAsB,CAAC,OAAgC;IAC9D,0DAA0D;IAC1D,IAAI,YAAY,GAAW,IAAI,CAAC;IAChC,KAAK,IAAI,CAAC,IAAI,mBAAmB,EAAE;QACjC,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;QAC9B,kBAAkB;QAClB,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;YACpB,YAAY,GAAG,CAAC,CAAC;YACjB,MAAM;SACP;KACF;IACD,IAAI,CAAC,YAAY;QACf,MAAM,IAAI,QAAQ,CAAC,yCAAyC,CAAC,CAAC;IAChE,IAAI,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,OAAO,YAAY,oBAAoB,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,cAAc,QAAQ,GAAG,CAAC,CAAC;IAEvC,oFAAoF;IACpF,qCAAqC;IACrC,IAAI,aAAa,GAAiC;QAChD,GAAG,EAAE;YACH,OAAO,EAAE,QAAQ;YACjB,IAAI,EAAE,CAAC,YAAY,CAAC;YACpB,OAAO,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE;SAC3B;QACD,KAAK,EAAE;YACL,OAAO,EAAE,QAAQ;YACjB,IAAI,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC;YAC/B,OAAO,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE;SAC3B;KACF,CAAC;IACF,yCAAyC;IACzC,IAAI,aAAa,GAAyC;QACxD,+CAA+C;QAC/C,gBAAgB,EAAE;YAChB;gBACE,QAAQ,EAAE,SAAS;gBACnB,MAAM,EAAE,MAAM;aACf;SACF;QACD,WAAW,EAAE;YACX,wEAAwE;YACxE,oBAAoB,EAAE,uBAAuB;YAC7C,oFAAoF;YACpF,UAAU,EAAE;gBACV,MAAM,CAAC,SAAS,CAAC,uBAAuB,CAAC,cAAc,CAAC;gBACxD,MAAM,CAAC,SAAS,CAAC,uBAAuB,CAAC,SAAS,CAAC;gBACnD,MAAM,CAAC,SAAS,CAAC,uBAAuB,CAAC,SAAS,CAAC;aACpD;SACF;KACF,CAAC;IAEF,mDAAmD;IACnD,IAAI,MAAM,GAAG,IAAI,cAAc,CAAC,cAAc,CAC5C,uBAAuB,EACvB,yBAAyB,EACzB,aAAa,EACb,aAAa,CACd,CAAC;IACF,IAAI,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;IAEhC,iEAAiE;IACjE,sDAAsD;IACtD,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACzC,CAAC;AAED,yDAAyD;AACzD,0EAA0E;AAC1E,SAAgB,QAAQ,CAAC,OAAgC;IACvD,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnD,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAClC,CAAC;AAHD,4BAGC;AAED,2DAA2D;AAC3D,SAAgB,UAAU,KAAI,CAAC;AAA/B,gCAA+B;AAC/B,CAAC,CAAC"}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"name": "upsilon-language-extension",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"vscode": "^1.29.1"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onLanguage:upsilon"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "upsilon",
|
||||
"aliases": [
|
||||
"Upsilon"
|
||||
],
|
||||
"extensions": [
|
||||
".yup",
|
||||
".lua"
|
||||
]
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "Example configuration",
|
||||
"properties": {
|
||||
"upsilonLanguageServer.maxNumberOfProblems": {
|
||||
"type": "number",
|
||||
"default": 100,
|
||||
"description": "Controls the maximum number of problems produced by the server."
|
||||
},
|
||||
"upsilonLanguageServer.trace.server": {
|
||||
"scope": "window",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"off",
|
||||
"messages",
|
||||
"verbose"
|
||||
],
|
||||
"default": "off",
|
||||
"description": "Traces the communication between VSCode and the upsilonLanguageServer service."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "vscode-install && npm run compile",
|
||||
"compile": "tsc -b",
|
||||
"watch": "tsc -b -w",
|
||||
"postinstall": "cd client && npm install && cd ../server && npm install && cd ..",
|
||||
"test": "sh ./scripts/e2e.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^4.1.4",
|
||||
"vscode": "^1.1.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^10.12.10",
|
||||
"vscode": "^1.1.21",
|
||||
"tslint": "^5.11.0",
|
||||
"typescript": "^3.1.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
"use strict";
|
||||
// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import * as languageClient from "vscode-languageclient";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
// Defines the search path of your language server DLL. (.NET Core)
|
||||
const languageServerPaths = [
|
||||
"server/DemoLanguageServer.dll",
|
||||
"../UpsilonLanguageServer/UpsilonLanguageServer/bin/Debug/netcoreapp2.1/UpsilonLanguageServer.dll"
|
||||
];
|
||||
|
||||
function activateLanguageServer(context: vscode.ExtensionContext) {
|
||||
// The server is implemented in an executable application.
|
||||
let serverModule: string = null;
|
||||
for (let p of languageServerPaths) {
|
||||
p = context.asAbsolutePath(p);
|
||||
// console.log(p);
|
||||
if (fs.existsSync(p)) {
|
||||
serverModule = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!serverModule)
|
||||
throw new URIError("Cannot find the language server module.");
|
||||
let workPath = path.dirname(serverModule);
|
||||
console.log(`Use ${serverModule} as server module.`);
|
||||
console.log(`Work path: ${workPath}.`);
|
||||
|
||||
// If the extension is launched in debug mode then the debug server options are used
|
||||
// Otherwise the run options are used
|
||||
let serverOptions: languageClient.ServerOptions = {
|
||||
run: {
|
||||
command: "dotnet",
|
||||
args: [serverModule],
|
||||
options: { cwd: workPath }
|
||||
},
|
||||
debug: {
|
||||
command: "dotnet",
|
||||
args: [serverModule, "--debug"],
|
||||
options: { cwd: workPath }
|
||||
}
|
||||
};
|
||||
// Options to control the language client
|
||||
let clientOptions: languageClient.LanguageClientOptions = {
|
||||
// Register the server for plain text documents
|
||||
documentSelector: [
|
||||
{
|
||||
language: "upsilon",
|
||||
scheme: "file"
|
||||
}
|
||||
],
|
||||
synchronize: {
|
||||
// Synchronize the setting section 'languageServerExample' to the server
|
||||
configurationSection: "upsilonLanguageServer",
|
||||
// Notify the server about file changes to '.clientrc files contain in the workspace
|
||||
fileEvents: [
|
||||
vscode.workspace.createFileSystemWatcher("**/.clientrc"),
|
||||
vscode.workspace.createFileSystemWatcher("**/.yup"),
|
||||
vscode.workspace.createFileSystemWatcher("**/.lua")
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
let client = new languageClient.LanguageClient(
|
||||
"upsilonLanguageServer",
|
||||
"Upsilon Language Server",
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
let disposable = client.start();
|
||||
|
||||
// Push the disposable to the context's subscriptions so that the
|
||||
// client can be deactivated on extension deactivation
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
||||
|
||||
// this method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log("Upsilon extension is now activated.");
|
||||
activateLanguageServer(context);
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() {}
|
||||
6;
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
"lib": ["es6"],
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", ".vscode-test"]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Subproject commit fc3c2f9dae32404e6556cf5bcc0b666fedbc3897
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
Binary file not shown.
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETStandard,Version=v2.0/",
|
||||
"signature": "7417c30610758d349c0e111cd15f02499f764b67"
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETStandard,Version=v2.0": {},
|
||||
".NETStandard,Version=v2.0/": {
|
||||
"Upsilon/1.0.0": {
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "2.0.3",
|
||||
"System.Collections.Immutable": "1.5.0"
|
||||
},
|
||||
"runtime": {
|
||||
"Upsilon.dll": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/1.1.0": {},
|
||||
"NETStandard.Library/2.0.3": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0"
|
||||
}
|
||||
},
|
||||
"System.Collections.Immutable/1.5.0": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/System.Collections.Immutable.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "4.6.26515.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"Upsilon/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
|
||||
"path": "microsoft.netcore.platforms/1.1.0",
|
||||
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"NETStandard.Library/2.0.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
|
||||
"path": "netstandard.library/2.0.3",
|
||||
"hashPath": "netstandard.library.2.0.3.nupkg.sha512"
|
||||
},
|
||||
"System.Collections.Immutable/1.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-RGxi2aQoXgZ5ge0zxrKqI4PU9LrYYoLC+cnEnWXKsSduCOUhE1GEAAoTexUVT8RZOILQyy1B27HC8Xw/XLGzdQ==",
|
||||
"path": "system.collections.immutable/1.5.0",
|
||||
"hashPath": "system.collections.immutable.1.5.0.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,16 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpsilonLanguageServer", "UpsilonLanguageServer\UpsilonLanguageServer.csproj", "{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,3 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/IsNamingAutoDetectionCompleted/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/IsNotificationDisabled/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using LanguageServer.VsCode.Server;
|
||||
using Upsilon;
|
||||
using Upsilon.Evaluator;
|
||||
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
public class DiagnosticProvider
|
||||
{
|
||||
|
||||
public DiagnosticProvider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static async Task<IEnumerable<Diagnostic>> LintDocument(TextDocument document, SessionDocument session,
|
||||
int maxNumberOfProblems)
|
||||
{
|
||||
var diag = new List<Diagnostic>();
|
||||
var content = document.Content;
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
diag.Add(new Diagnostic(DiagnosticSeverity.Hint,
|
||||
new Range(new Position(0, 0), document.PositionAt(content?.Length ?? 0)),
|
||||
"DLS", "DLS0001", "Empty document. Try typing something, such as \".net core\"."));
|
||||
return diag;
|
||||
}
|
||||
|
||||
var task = RealLint(document, maxNumberOfProblems, session, content, diag);
|
||||
// either wait until the script is linted, or until a second has passed (timeout)
|
||||
await Task.WhenAny(task, Task.Delay(1000));
|
||||
return diag;
|
||||
}
|
||||
|
||||
private static async Task RealLint(TextDocument document, int maxNumberOfProblems, SessionDocument session,
|
||||
string content, List<Diagnostic> diag)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var script = new Script(content))
|
||||
{
|
||||
session.SourceText = script.ScriptString;
|
||||
if (script.Diagnostics.Messages.Count > 0)
|
||||
{
|
||||
foreach (var error in script.Diagnostics.Messages)
|
||||
{
|
||||
diag.Add(ConvertToVsCodeDiagnostic(error));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var bound = script.Bind();
|
||||
foreach (var error in script.Diagnostics.Messages)
|
||||
{
|
||||
diag.Add(ConvertToVsCodeDiagnostic(error));
|
||||
}
|
||||
|
||||
if (script.Diagnostics.Messages.Count == 0)
|
||||
{
|
||||
session.Bound = bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
diag.Add(new Diagnostic(DiagnosticSeverity.Error,
|
||||
new Range(new Position(0, 0), document.PositionAt(content?.Length ?? 0)),
|
||||
"DLS", "DLS0001", e.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
private static Diagnostic ConvertToVsCodeDiagnostic(DiagnosticsMessage error)
|
||||
{
|
||||
var (startPosition, startPositionOnLine) = error.GetStartLine();
|
||||
var (endPosition, endPositionOnLine) = error.GetEndLine();
|
||||
var sourceString = error.Diagnostics.ScriptString;
|
||||
if (endPosition >= sourceString.GetLineCount())
|
||||
endPosition = sourceString.GetLineCount();
|
||||
|
||||
var range = new Range(startPosition, startPositionOnLine, endPosition,
|
||||
endPositionOnLine);
|
||||
return new Diagnostic(DiagnosticSeverity.Error, range, error.AtError(), "ERR001", error.Message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JsonRpc.DynamicProxy.Client;
|
||||
using JsonRpc.Standard.Client;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using LanguageServer.VsCode.Contracts.Client;
|
||||
using LanguageServer.VsCode.Server;
|
||||
using Upsilon.Binder;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
public class LanguageServerSession
|
||||
{
|
||||
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
|
||||
|
||||
public LanguageServerSession(JsonRpcClient rpcClient, IJsonRpcContractResolver contractResolver)
|
||||
{
|
||||
RpcClient = rpcClient ?? throw new ArgumentNullException(nameof(rpcClient));
|
||||
var builder = new JsonRpcProxyBuilder {ContractResolver = contractResolver};
|
||||
Client = new ClientProxy(builder, rpcClient);
|
||||
Documents = new ConcurrentDictionary<Uri, SessionDocument>();
|
||||
DiagnosticProvider = new DiagnosticProvider();
|
||||
}
|
||||
|
||||
public CancellationToken CancellationToken => _cts.Token;
|
||||
|
||||
public JsonRpcClient RpcClient { get; }
|
||||
|
||||
public ClientProxy Client { get; }
|
||||
|
||||
public ConcurrentDictionary<Uri, SessionDocument> Documents { get; }
|
||||
|
||||
public DiagnosticProvider DiagnosticProvider { get; }
|
||||
|
||||
public LanguageServerSettings Settings { get; set; } = new LanguageServerSettings();
|
||||
|
||||
public void StopServer()
|
||||
{
|
||||
_cts.Cancel();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SessionDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// Actually makes the changes to the inner document per this milliseconds.
|
||||
/// </summary>
|
||||
private const int RenderChangesDelay = 100;
|
||||
|
||||
public SessionDocument(TextDocumentItem doc)
|
||||
{
|
||||
Document = TextDocument.Load<FullTextDocument>(doc);
|
||||
}
|
||||
|
||||
private Task _updateChangesDelayTask;
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
private List<TextDocumentContentChangeEvent> _impendingChanges = new List<TextDocumentContentChangeEvent>();
|
||||
|
||||
public event EventHandler DocumentChanged;
|
||||
|
||||
public TextDocument Document { get; set; }
|
||||
|
||||
public BoundScript Bound { get; set; }
|
||||
public SourceText SourceText { get; set; }
|
||||
|
||||
public void NotifyChanges(IEnumerable<TextDocumentContentChangeEvent> changes)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_impendingChanges == null)
|
||||
_impendingChanges = changes.ToList();
|
||||
else
|
||||
_impendingChanges.AddRange(changes);
|
||||
}
|
||||
if (_updateChangesDelayTask == null || _updateChangesDelayTask.IsCompleted)
|
||||
{
|
||||
_updateChangesDelayTask = Task.Delay(RenderChangesDelay);
|
||||
_updateChangesDelayTask.ContinueWith(t => Task.Run((Action)MakeChanges));
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeChanges()
|
||||
{
|
||||
List<TextDocumentContentChangeEvent> localChanges;
|
||||
lock (_syncLock)
|
||||
{
|
||||
localChanges = _impendingChanges;
|
||||
if (localChanges == null || localChanges.Count == 0) return;
|
||||
_impendingChanges = null;
|
||||
}
|
||||
Document = Document.ApplyChanges(localChanges);
|
||||
if (_impendingChanges == null)
|
||||
{
|
||||
localChanges.Clear();
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_impendingChanges == null)
|
||||
_impendingChanges = localChanges;
|
||||
}
|
||||
}
|
||||
OnDocumentChanged();
|
||||
}
|
||||
|
||||
protected virtual void OnDocumentChanged()
|
||||
{
|
||||
DocumentChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// #define WAIT_FOR_DEBUGGER
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using JsonRpc.Standard.Client;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using JsonRpc.Standard.Server;
|
||||
using JsonRpc.Streams;
|
||||
using LanguageServer.VsCode;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Debug;
|
||||
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var debugMode = args.Any(a => a.Equals("--debug", StringComparison.OrdinalIgnoreCase));
|
||||
#if WAIT_FOR_DEBUGGER
|
||||
while (!Debugger.IsAttached) Thread.Sleep(1000);
|
||||
Debugger.Break();
|
||||
#endif
|
||||
StreamWriter logWriter = null;
|
||||
if (debugMode)
|
||||
{
|
||||
logWriter = File.CreateText("messages-" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".log");
|
||||
logWriter.AutoFlush = true;
|
||||
}
|
||||
using (logWriter)
|
||||
using (var cin = Console.OpenStandardInput())
|
||||
using (var bcin = new BufferedStream(cin))
|
||||
using (var cout = Console.OpenStandardOutput())
|
||||
using (var reader = new PartwiseStreamMessageReader(bcin))
|
||||
using (var writer = new PartwiseStreamMessageWriter(cout))
|
||||
{
|
||||
var contractResolver = new JsonRpcContractResolver
|
||||
{
|
||||
NamingStrategy = new CamelCaseJsonRpcNamingStrategy(),
|
||||
ParameterValueConverter = new CamelCaseJsonValueConverter(),
|
||||
};
|
||||
var clientHandler = new StreamRpcClientHandler();
|
||||
var client = new JsonRpcClient(clientHandler);
|
||||
if (debugMode)
|
||||
{
|
||||
// We want to capture log all the LSP server-to-client calls as well
|
||||
clientHandler.MessageSending += (_, e) =>
|
||||
{
|
||||
lock (logWriter) logWriter.WriteLine("{0} <C{1}", Utility.GetTimeStamp(), e.Message);
|
||||
};
|
||||
clientHandler.MessageReceiving += (_, e) =>
|
||||
{
|
||||
lock (logWriter) logWriter.WriteLine("{0} >C{1}", Utility.GetTimeStamp(), e.Message);
|
||||
};
|
||||
}
|
||||
// Configure & build service host
|
||||
var session = new LanguageServerSession(client, contractResolver);
|
||||
var host = BuildServiceHost(logWriter, contractResolver, debugMode);
|
||||
var serverHandler = new StreamRpcServerHandler(host,
|
||||
StreamRpcServerHandlerOptions.ConsistentResponseSequence |
|
||||
StreamRpcServerHandlerOptions.SupportsRequestCancellation);
|
||||
serverHandler.DefaultFeatures.Set(session);
|
||||
// If we want server to stop, just stop the "source"
|
||||
using (serverHandler.Attach(reader, writer))
|
||||
using (clientHandler.Attach(reader, writer))
|
||||
{
|
||||
// Wait for the "stop" request.
|
||||
session.CancellationToken.WaitHandle.WaitOne();
|
||||
}
|
||||
logWriter?.WriteLine("Exited");
|
||||
}
|
||||
}
|
||||
|
||||
private static IJsonRpcServiceHost BuildServiceHost(TextWriter logWriter,
|
||||
IJsonRpcContractResolver contractResolver, bool debugMode)
|
||||
{
|
||||
var loggerFactory = new LoggerFactory();
|
||||
loggerFactory.AddProvider(new DebugLoggerProvider(null));
|
||||
var builder = new JsonRpcServiceHostBuilder
|
||||
{
|
||||
ContractResolver = contractResolver,
|
||||
LoggerFactory = loggerFactory
|
||||
};
|
||||
builder.UseCancellationHandling();
|
||||
builder.Register(typeof(Program).GetTypeInfo().Assembly);
|
||||
if (debugMode)
|
||||
{
|
||||
// Log all the client-to-server calls.
|
||||
builder.Intercept(async (context, next) =>
|
||||
{
|
||||
lock (logWriter) logWriter.WriteLine("{0} > {1}", Utility.GetTimeStamp(), context.Request);
|
||||
await next();
|
||||
lock (logWriter) logWriter.WriteLine("{0} < {1}", Utility.GetTimeStamp(), context.Response);
|
||||
});
|
||||
}
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using JsonRpc.Standard.Contracts;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
[JsonRpcScope(MethodPrefix = "completionItem/")]
|
||||
public class CompletionItemService : UpsilonLanguageServiceBase
|
||||
{
|
||||
// The request is sent from the client to the server to resolve additional information
|
||||
// for a given completion item.
|
||||
[JsonRpcMethod(AllowExtensionData = true)]
|
||||
public CompletionItem Resolve()
|
||||
{
|
||||
var item = RequestContext.Request.Parameters.ToObject<CompletionItem>(Utility.CamelCaseJsonSerializer);
|
||||
// Add a pair of square brackets around the inserted text.
|
||||
item.InsertText = $"[{item.Label}]";
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JsonRpc.Standard;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using JsonRpc.Standard.Server;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using Newtonsoft.Json.Linq;
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
public class InitializationService : UpsilonLanguageServiceBase
|
||||
{
|
||||
|
||||
[JsonRpcMethod(AllowExtensionData = true)]
|
||||
public InitializeResult Initialize(int processId, Uri rootUri, ClientCapabilities capabilities,
|
||||
JToken initializationOptions = null, string trace = null)
|
||||
{
|
||||
return new InitializeResult(new ServerCapabilities
|
||||
{
|
||||
HoverProvider = true,
|
||||
SignatureHelpProvider = new SignatureHelpOptions("()"),
|
||||
CompletionProvider = new CompletionOptions(true, "."),
|
||||
TextDocumentSync = new TextDocumentSyncOptions
|
||||
{
|
||||
OpenClose = true,
|
||||
WillSave = true,
|
||||
Change = TextDocumentSyncKind.Incremental
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task Initialized()
|
||||
{
|
||||
await Client.Window.ShowMessage(MessageType.Info, "Upsilon Language Server Initialized");
|
||||
}
|
||||
|
||||
[JsonRpcMethod]
|
||||
public void Shutdown()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public void Exit()
|
||||
{
|
||||
Client.Window.ShowMessage(MessageType.Info, "Upsilon Language Server exited");
|
||||
Session.StopServer();
|
||||
}
|
||||
|
||||
[JsonRpcMethod("$/cancelRequest", IsNotification = true)]
|
||||
public void CancelRequest(MessageId id)
|
||||
{
|
||||
RequestContext.Features.Get<IRequestCancellationFeature>().TryCancel(id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using LanguageServer.VsCode;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using LanguageServer.VsCode.Server;
|
||||
using Upsilon.Binder;
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
[JsonRpcScope(MethodPrefix = "textDocument/")]
|
||||
public class TextDocumentService : UpsilonLanguageServiceBase
|
||||
{
|
||||
[JsonRpcMethod]
|
||||
public async Task<Hover> Hover(TextDocumentIdentifier textDocument, Position position, CancellationToken ct)
|
||||
{
|
||||
// Note that Hover is cancellable.
|
||||
//await Task.Delay(1000, ct);
|
||||
if (Session.Documents.TryGetValue(textDocument.Uri, out var doc))
|
||||
{
|
||||
if (doc.Bound != null && doc.SourceText != null)
|
||||
{
|
||||
var linePos = doc.SourceText.GetLineStartPos(position.Line);
|
||||
var findNode = doc.Bound.GetNodeAtPosition(linePos + position.Character);
|
||||
if (findNode != null)
|
||||
{
|
||||
var contents = $"Kind: {findNode.Kind}";
|
||||
if (findNode is BoundExpression expression)
|
||||
{
|
||||
contents += $"\n\nType: {expression.Type}";
|
||||
}
|
||||
|
||||
return new Hover()
|
||||
{
|
||||
Contents = contents
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Hover();
|
||||
}
|
||||
|
||||
[JsonRpcMethod]
|
||||
public SignatureHelp SignatureHelp(TextDocumentIdentifier textDocument, Position position)
|
||||
{
|
||||
return new SignatureHelp(new List<SignatureInformation>
|
||||
{
|
||||
new SignatureInformation("**Function1**", "Documentation1"),
|
||||
new SignatureInformation("**Function2** <strong>test</strong>", "Documentation2"),
|
||||
});
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task DidOpen(TextDocumentItem textDocument)
|
||||
{
|
||||
var doc = new SessionDocument(textDocument);
|
||||
var session = Session;
|
||||
doc.DocumentChanged += async (sender, args) =>
|
||||
{
|
||||
// Lint the document when it's changed.
|
||||
var sessionDocument = ((SessionDocument) sender);
|
||||
var doc1 = sessionDocument.Document;
|
||||
var diag1 = DiagnosticProvider.LintDocument(doc1, sessionDocument, session.Settings.MaxNumberOfProblems);
|
||||
if (session.Documents.ContainsKey(doc1.Uri))
|
||||
{
|
||||
// In case the document has been closed when we were linting…
|
||||
await session.Client.Document.PublishDiagnostics(doc1.Uri, await diag1);
|
||||
}
|
||||
};
|
||||
Session.Documents.TryAdd(textDocument.Uri, doc);
|
||||
var diag = DiagnosticProvider.LintDocument(doc.Document, doc, Session.Settings.MaxNumberOfProblems);
|
||||
await Client.Document.PublishDiagnostics(textDocument.Uri, await diag);
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public void DidChange(TextDocumentIdentifier textDocument,
|
||||
ICollection<TextDocumentContentChangeEvent> contentChanges)
|
||||
{
|
||||
Session.Documents[textDocument.Uri].NotifyChanges(contentChanges);
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public void WillSave(TextDocumentIdentifier textDocument, TextDocumentSaveReason reason)
|
||||
{
|
||||
//Client.Window.LogMessage(MessageType.Log, "-----------");
|
||||
//Client.Window.LogMessage(MessageType.Log, Documents[textDocument].Content);
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task DidClose(TextDocumentIdentifier textDocument)
|
||||
{
|
||||
if (textDocument.Uri.IsUntitled())
|
||||
{
|
||||
await Client.Document.PublishDiagnostics(textDocument.Uri, new Diagnostic[0]);
|
||||
}
|
||||
Session.Documents.TryRemove(textDocument.Uri, out _);
|
||||
}
|
||||
|
||||
private static readonly CompletionItem[] PredefinedCompletionItems =
|
||||
{
|
||||
new CompletionItem(".NET", CompletionItemKind.Keyword,
|
||||
"Keyword1",
|
||||
"Short for **.NET Framework**, a software framework by Microsoft (possibly its subsets) or later open source .NET Core.",
|
||||
null),
|
||||
new CompletionItem(".NET Standard", CompletionItemKind.Keyword,
|
||||
"Keyword2",
|
||||
"The .NET Standard is a formal specification of .NET APIs that are intended to be available on all .NET runtimes.",
|
||||
null),
|
||||
new CompletionItem(".NET Framework", CompletionItemKind.Keyword,
|
||||
"Keyword3",
|
||||
".NET Framework (pronounced dot net) is a software framework developed by Microsoft that runs primarily on Microsoft Windows.", null),
|
||||
};
|
||||
|
||||
[JsonRpcMethod]
|
||||
public async Task<CompletionList> Completion(TextDocumentIdentifier textDocument, Position position, CompletionContext context)
|
||||
{
|
||||
return new CompletionList(PredefinedCompletionItems, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using JsonRpc.Standard.Server;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using LanguageServer.VsCode.Contracts.Client;
|
||||
using LanguageServer.VsCode.Server;
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
public class UpsilonLanguageServiceBase : JsonRpcService
|
||||
{
|
||||
protected LanguageServerSession Session => RequestContext.Features.Get<LanguageServerSession>();
|
||||
|
||||
protected ClientProxy Client => Session.Client;
|
||||
|
||||
protected TextDocument GetDocument(Uri uri)
|
||||
{
|
||||
if (Session.Documents.TryGetValue(uri, out var sd))
|
||||
return sd.Document;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected TextDocument GetDocument(TextDocumentIdentifier id) => GetDocument(id.Uri);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
[JsonRpcScope(MethodPrefix = "workspace/")]
|
||||
public class WorkspaceService : UpsilonLanguageServiceBase
|
||||
{
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task DidChangeConfiguration(SettingsRoot settings)
|
||||
{
|
||||
Session.Settings = settings.UpsilonLanguageServer;
|
||||
foreach (var doc in Session.Documents.Values)
|
||||
{
|
||||
var diag = await DiagnosticProvider.LintDocument(doc.Document, doc, Session.Settings.MaxNumberOfProblems);
|
||||
await Client.Document.PublishDiagnostics(doc.Document.Uri, diag);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task DidChangeWatchedFiles(ICollection<FileEvent> changes)
|
||||
{
|
||||
foreach (var change in changes)
|
||||
{
|
||||
if (!change.Uri.IsFile) continue;
|
||||
var localPath = change.Uri.AbsolutePath;
|
||||
if (string.Equals(Path.GetExtension(localPath), ".yup", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// If the file has been removed, we will clear the lint result about it.
|
||||
// Note that pass null to PublishDiagnostics may mess up the client.
|
||||
if (change.Type == FileChangeType.Deleted)
|
||||
{
|
||||
await Client.Document.PublishDiagnostics(change.Uri, new Diagnostic[0]);
|
||||
}
|
||||
}
|
||||
else if (string.Equals(Path.GetExtension(localPath), ".lua", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// If the file has been removed, we will clear the lint result about it.
|
||||
// Note that pass null to PublishDiagnostics may mess up the client.
|
||||
if (change.Type == FileChangeType.Deleted)
|
||||
{
|
||||
await Client.Document.PublishDiagnostics(change.Uri, new Diagnostic[0]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
namespace UpsilonLanguageServer
|
||||
{
|
||||
public class SettingsRoot
|
||||
{
|
||||
public LanguageServerSettings UpsilonLanguageServer { get; set; }
|
||||
}
|
||||
|
||||
public class LanguageServerSettings
|
||||
{
|
||||
public int MaxNumberOfProblems { get; set; } = 10;
|
||||
|
||||
public LanguageServerTraceSettings Trace { get; } = new LanguageServerTraceSettings();
|
||||
}
|
||||
|
||||
public class LanguageServerTraceSettings
|
||||
{
|
||||
public string Server { get; set; }
|
||||
}}
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CXuesong.JsonRpc.Streams" Version="0.4.2" />
|
||||
<PackageReference Include="CXuesong.LanguageServer.VsCode" Version="0.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.TraceSource" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Collections.Immutable, Version=1.2.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\Lib\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Upsilon, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>..\Lib\Upsilon.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
public static class Utility
|
||||
{
|
||||
public static readonly JsonSerializer CamelCaseJsonSerializer = new JsonSerializer
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
|
||||
public static string GetTimeStamp()
|
||||
{
|
||||
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue