[ADF-3093] Added style checking tool for en.json translation file (#3428)

* [ADF-3093] Started li18nt VS Code extension

* [ADF-3093] Started work on UI style lint tool for VSCode

* [ADF-3093] Added UI style rules up to sg0006

* [ADF-3093] Added remaining style rules

* [ADF-3093] Added docs and command line tool

* [ADF-3093] Removed Microsoft notices and updated licences to Apache-2.0
This commit is contained in:
Andy Stark
2018-06-05 17:49:52 +02:00
committed by Eugenio Romano
parent 4225bf5213
commit dfc83489e2
21 changed files with 2667 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
out
node_modules
client/server
+32
View File
@@ -0,0 +1,32 @@
{
"version": "0.2.0",
// List of configurations. Add new configurations or edit existing ones.
"configurations": [
{
"name": "Launch Client",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}/client"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/client/out/**/*.js"
],
"preLaunchTask": "watch:client"
},
{
"name": "Attach to Server",
"type": "node",
"request": "attach",
"port": 6009,
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/client/server/**/*.js"
],
"preLaunchTask": "watch:server"
}
]
}
+15
View File
@@ -0,0 +1,15 @@
{
// set to false to hide directory from the VS Code Document Sidebar
"files.exclude": {
"client/out/**/*": false,
"client/server/**/*": false,
"client/node_modules": false,
"server/node_modules": false
},
// set this to false to include "out" folder in search results
"search.exclude": {
"out": true
},
"typescript.tsdk": "./node_modules/typescript/lib",
"typescript.tsc.autoDetect": "off"
}
+79
View File
@@ -0,0 +1,79 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "compile",
"dependsOn": [
"compile:client",
"compile:server"
],
"problemMatcher": []
},
{
"label": "compile:client",
"type": "npm",
"script": "compile:client",
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$tsc"
]
},
{
"label": "compile:server",
"type": "npm",
"script": "compile:server",
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$tsc"
]
},
{
"label": "watch",
"dependsOn": [
"watch:client",
"watch:server"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "watch:client",
"type": "npm",
"script": "watch:client",
"isBackground": true,
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$tsc-watch"
]
},
{
"label": "watch:server",
"type": "npm",
"script": "watch:server",
"isBackground": true,
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$tsc-watch"
]
}
]
}
+36
View File
@@ -0,0 +1,36 @@
# li18nt UI text style checker
The purpose of this tool is to provide style "lint" checking for the en.json
file, which is used as the starting point for i18n in the user interface. The
definitive guidelines can be found in the main
[style guide](https://docs.alfresco.com/sites/docs.alfresco.com/files/public/docs_team/u2/Alfresco-Writing-Guide.pdf) document. This tool implements the guidelines as a set of rules that can be
checked automatically for common errors (the [style rules](../UIStyleRules.md) file
contains a full description of the rules currently in use).
## Installing and using the VSCode extension
The VS Code extension shows the style warnings as underlined sections in the text
with corresponding notes in the Problems window, a lot like standard programming
language errors.
The extension is not available in the VS Code marketplace but you can install it
locally by copying it to the extensions folder. This can be found at the path
`$HOME/.vscode/extensions`
...on MacOSX and at
`%USERPROFILE%\.vscode\extensions`
...on Windows. Copy the `client` folder (from `alfresco-ng2-components/tools/i18n/li18nt/`)
to the extensions folder and rename it to `li18nt`. If there is no `node_modules`
folder in the new `li18nt` folder then you should also `cd` into this folder and
run `npm install`. When you restart VS Code, you should find `li18nt` listed among
the installed extensions in the extensions panel.
When active, the extension will only check the text of files named `en.json`.
Double-click an item from the Problems window in VS Code to highlight the section
of text where the issue occurs. You can find out more about why the error has
occurred and what to do about it in the [style rules](../UIStyleRules.md) file.
For the full description and explanation of all style guidelines, see the main
[style guide](https://docs.alfresco.com/sites/docs.alfresco.com/files/public/docs_team/u2/Alfresco-Writing-Guide.pdf).
+9
View File
@@ -0,0 +1,9 @@
.vscode/**
typings/**
out/test/**
test/**
src/**
**/*.map
.gitignore
tsconfig.json
vsc-extension-quickstart.md
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
{
"name": "li18nt",
"description": "Style checker for ADF translation files.",
"author": "Alfresco Software",
"license": "Apache-2.0",
"version": "0.0.1",
"publisher": "vscode",
"repository": {
"type": "git",
"url": "https://github.com/Alfresco/alfresco-ng2-components"
},
"engines": {
"vscode": "^1.22.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onLanguage:json"
],
"main": "./out/src/extension",
"contributes": {
"configuration": {
"type": "object",
"title": "Main configuration",
"properties": {
"li18nt.maxNumberOfProblems": {
"scope": "resource",
"type": "number",
"default": 100,
"description": "Controls the maximum number of problems produced by the server."
},
"li18nt.trace.server": {
"scope": "window",
"type": "string",
"enum": [
"off",
"messages",
"verbose"
],
"default": "off",
"description": "Traces the communication between VSCode and the language server."
}
}
}
},
"scripts": {
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -p ./",
"watch": "tsc -w -p ./",
"update-vscode": "node ./node_modules/vscode/bin/install",
"postinstall": "node ./node_modules/vscode/bin/install"
},
"dependencies": {
"vscode": "^1.1.17",
"vscode-languageclient": "^4.1.3"
}
}
+40
View File
@@ -0,0 +1,40 @@
'use strict';
import * as path from 'path';
import { workspace, ExtensionContext } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
export function activate(context: ExtensionContext) {
// The server is implemented in node
let serverModule = context.asAbsolutePath(path.join('server', 'server.js'));
// The debug options for the server
let debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] };
// If the extension is launched in debug mode then the debug server options are used
// Otherwise the run options are used
let serverOptions: ServerOptions = {
run : { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
}
// Options to control the language client
let clientOptions: LanguageClientOptions = {
// Register the server for plain text documents
documentSelector: [{scheme: 'file', language: 'json'}],
synchronize: {
// Synchronize the setting section 'languageServerExample' to the server
configurationSection: 'li18nt',
// Notify the server about file changes to '.clientrc files contain in the workspace
fileEvents: workspace.createFileSystemWatcher('**/.clientrc')
}
}
// Create the language client and start the client.
let disposable = new LanguageClient('li18nt', 'li18nt', serverOptions, clientOptions).start();
// Push the disposable to the context's subscriptions so that the
// client can be deactivated on extension deactivation
context.subscriptions.push(disposable);
}
+19
View File
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"rootDir": ".",
"outDir": "out",
"lib": [ "es2016" ],
"sourceMap": true
},
"exclude": [
"node_modules",
"server"
]
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "lsp--sample",
"version": "0.0.1",
"lockfileVersion": 1
}
+21
View File
@@ -0,0 +1,21 @@
{
"name": "li18nt",
"description": "Style checker for ADF translation files",
"author": "Alfresco Software",
"license": "Apache-2.0",
"version": "0.0.1",
"publisher": "vscode",
"repository": {
"type": "git",
"url": "https://github.com/Alfresco/alfresco-ng2-components"
},
"scripts": {
"postinstall": "cd server && npm install && cd ../client && npm install && cd ..",
"compile": "tsc -p client/tsconfig.json && cd server && npm run installServer && cd .. && tsc -p server/tsconfig.json",
"compile:client": "tsc -p client/tsconfig.json",
"watch:client": "tsc -w -p client/tsconfig.json",
"compile:server": "cd server && npm run installServer && cd .. && tsc -p server/tsconfig.json",
"watch:server": "cd server && npm run installServer && cd .. && tsc -w -p server/tsconfig.json"
},
"devDependencies": {}
}
+41
View File
@@ -0,0 +1,41 @@
{
"name": "lsp-sample",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"vscode-jsonrpc": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz",
"integrity": "sha512-T24Jb5V48e4VgYliUXMnZ379ItbrXgOimweKaJshD84z+8q7ZOZjJan0MeDe+Ugb+uqERDVV8SBmemaGMSMugA=="
},
"vscode-languageserver": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-4.1.3.tgz",
"integrity": "sha512-D6p3q9x8QPtPLRUO5d2UKizjFYfg8zLVJqKoMpAaom8Wuhl1oKRCjeLg+Cp4mgPeCwR71wbgX2BM/jL51ni/0g==",
"requires": {
"vscode-languageserver-protocol": "3.7.2",
"vscode-uri": "1.0.3"
}
},
"vscode-languageserver-protocol": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.7.2.tgz",
"integrity": "sha512-VVJwIA/FPl/FnVtrns0FPK6TLi/ET7n1Yo6tCrm6aG7+yAVwIGWdpTmKE+nbP8wEMMbHCkIabk63IJvfz2HNRg==",
"requires": {
"vscode-jsonrpc": "3.6.2",
"vscode-languageserver-types": "3.7.2"
}
},
"vscode-languageserver-types": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz",
"integrity": "sha512-L9D2RA+PDS2CiyhLQY5ZrOmyRvXyjc4Ha8s9PqS6mIgGxj00R5Xx2vLKBnAOVfrawJXYZST+2hioMks6SQVU7A=="
},
"vscode-uri": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.3.tgz",
"integrity": "sha1-Yxvb9xbcyrDmUpGo3CXCMjIIWlI="
}
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"name": "li18nt",
"description": "Style checker for ADF translation files.",
"version": "0.0.1",
"author": "Alfresco Software",
"license": "Apache-2.0",
"engines": {
"node": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/Alfresco/alfresco-ng2-components"
},
"dependencies": {
"vscode-languageserver": "^4.1.2"
},
"scripts": {
"installServer": "installServerIntoExtension ../client ./package.json ./tsconfig.json",
"compile": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -p .",
"watch": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -w -p ."
}
}
@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var SGStringProblem = /** @class */ (function () {
function SGStringProblem(messageCode, lineNum, startCharPos, endCharPos) {
this.messageCode = messageCode;
this.lineNum = lineNum;
this.startCharPos = startCharPos;
this.endCharPos = endCharPos;
}
return SGStringProblem;
}());
exports.SGStringProblem = SGStringProblem;
exports.sgErrorMessages = {
'SG0001': 'Polite words are not recommended for UI text',
'SG0002': 'Avoid using interjections in UI text ("oops", "yeah", "wow")',
'SG0003': 'Avoid exclamations ("!")',
'SG0004': 'Avoid unusual punctuation marks (";", "~", "^")',
'SG0005': 'Don\'t use the ampersand ("&") as a replacement for "and"',
'SG0006': 'Write numbers using digits instead of words',
'SG0007': 'Contractions ("can\'t", "won\'t" "isn\'t") are usually better than the equivalent phrase',
'SG0008': 'Leave out trademark and copyright symbols',
'SG0009': 'Avoid abbreviations for common phrases ("etc", "e.g.")'
};
exports.rules = [
sg0001, sg0002, sg0003, sg0004, sg0005, sg0006, sg0007, sg0008, sg0009
];
function sg0001(line, lineNum) {
// Use global regex to allow repeated searching from the end of the
// previous match.
var checkWords = /\b(please|thanks|thank you|sorry)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0001");
}
function sg0002(line, lineNum) {
var checkWords = /\b(whoops|oops|wow|yeah|hey|oh|aw)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0002");
}
function sg0003(line, lineNum) {
var checkWords = /!+/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0003");
}
function sg0004(line, lineNum) {
var checkWords = /[;~\^]+/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0004");
}
function sg0005(line, lineNum) {
var checkWords = /&+/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0005");
}
function sg0006(line, lineNum) {
var checkWords = /\b(two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety|hundred|thousand)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0006");
}
function sg0007(line, lineNum) {
var checkWords = /\b(cannot|do not|will not|have not|is not|should not|you are|we will)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0007");
}
function sg0008(line, lineNum) {
var checkWords = /[\u00a9\u00ae\u2122]+/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0008");
}
function sg0009(line, lineNum) {
var checkWords = /\b(etc|e\.g\.|eg|i\.e\.|ie|n\.b\.|nb)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0009");
}
function checkForRegExpMatches(re, line, lineNum, ruleName) {
var problems = [];
var matchInfo = re.exec(line);
while (matchInfo) {
problems.push(new SGStringProblem(ruleName, lineNum, matchInfo.index, matchInfo.index + matchInfo[0].length));
matchInfo = re.exec(line);
}
return problems;
}
@@ -0,0 +1,108 @@
export class SGStringProblem {
constructor(
public messageCode: string,
public lineNum: number,
public startCharPos: number,
public endCharPos: number
){}
}
export let sgErrorMessages: {[index: string]: string} = {
'SG0001': 'Polite words are not recommended for UI text',
'SG0002': 'Avoid using interjections in UI text ("oops", "yeah", "wow")',
'SG0003': 'Avoid exclamations ("!")',
'SG0004': 'Avoid unusual punctuation marks (";", "~", "^")',
'SG0005': 'Don\'t use the ampersand ("&") as a replacement for "and"',
'SG0006': 'Write numbers using digits instead of words',
'SG0007': 'Contractions ("can\'t", "won\'t" "isn\'t") are usually better than the equivalent phrase',
'SG0008': 'Leave out trademark and copyright symbols',
'SG0009': 'Avoid abbreviations for common phrases ("etc", "e.g.")'
};
export type SGRule = (line: string, lineNum: number) => SGStringProblem[];
export let rules: SGRule[] = [
sg0001, sg0002, sg0003, sg0004, sg0005, sg0006, sg0007, sg0008, sg0009
];
function sg0001(line: string, lineNum: number): SGStringProblem[] {
// Use global regex to allow repeated searching from the end of the
// previous match.
let checkWords = /\b(please|thanks|thank you|sorry)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0001");
}
function sg0002(line: string, lineNum: number): SGStringProblem[] {
let checkWords = /\b(whoops|oops|wow|yeah|hey|oh|aw)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0002");
}
function sg0003(line: string, lineNum: number): SGStringProblem[] {
let checkWords = /!+/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0003");
}
function sg0004(line: string, lineNum: number): SGStringProblem[] {
let checkWords = /[;~\^]+/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0004");
}
function sg0005(line: string, lineNum: number): SGStringProblem[] {
let checkWords = /&+/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0005");
}
function sg0006(line: string, lineNum: number): SGStringProblem[] {
let checkWords = /\b(two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety|hundred|thousand)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0006");
}
function sg0007(line: string, lineNum: number): SGStringProblem[] {
let checkWords = /\b(cannot|do not|will not|have not|is not|should not|you are|we will)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0007");
}
function sg0008(line: string, lineNum: number): SGStringProblem[] {
let checkWords = /[\u00a9\u00ae\u2122]+/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0008");
}
function sg0009(line: string, lineNum: number): SGStringProblem[] {
let checkWords = /\b(etc|e\.g\.|eg|i\.e\.|ie|n\.b\.|nb)\b/gi;
return checkForRegExpMatches(checkWords, line, lineNum, "SG0009");
}
function checkForRegExpMatches(re: RegExp, line: string, lineNum: number, ruleName: string): SGStringProblem[] {
let problems: SGStringProblem[] = [];
let matchInfo = re.exec(line);
while (matchInfo) {
problems.push(
new SGStringProblem(
ruleName,
lineNum,
matchInfo.index,
matchInfo.index + matchInfo[0].length
)
);
matchInfo = re.exec(line);
}
return problems;
}
+170
View File
@@ -0,0 +1,170 @@
'use strict';
import {
IPCMessageReader, IPCMessageWriter, createConnection, IConnection, TextDocuments, TextDocument,
Diagnostic, DiagnosticSeverity, InitializeResult, TextDocumentPositionParams, CompletionItem,
CompletionItemKind
} from 'vscode-languageserver';
import {
SGStringProblem, sgErrorMessages, rules
} from './SGStyleRules';
// Create a connection for the server. The connection uses Node's IPC as a transport
let connection: IConnection = createConnection(new IPCMessageReader(process), new IPCMessageWriter(process));
// Create a simple text document manager. The text document manager
// supports full document sync only
let documents: TextDocuments = new TextDocuments();
// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);
//let shouldSendDiagnosticRelatedInformation: boolean = false;
// After the server has started the client sends an initialize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
connection.onInitialize((_params): InitializeResult => {
//shouldSendDiagnosticRelatedInformation = _params.capabilities && _params.capabilities.textDocument && _params.capabilities.textDocument.publishDiagnostics && _params.capabilities.textDocument.publishDiagnostics.relatedInformation;
return {
capabilities: {
// Tell the client that the server works in FULL text document sync mode
textDocumentSync: documents.syncKind,
// Tell the client that the server support code complete
completionProvider: {
resolveProvider: true
}
}
}
});
// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent((change) => {
if (change.document.uri.endsWith('en.json')) {
validateTextDocument(change.document);
}
});
// The settings interface describe the server relevant settings part
interface Settings {
li18nt: ExampleSettings;
}
// These are the example settings we defined in the client's package.json
// file
interface ExampleSettings {
maxNumberOfProblems: number;
}
// hold the maxNumberOfProblems setting
let maxNumberOfProblems: number;
// The settings have changed. Is send on server activation
// as well.
connection.onDidChangeConfiguration((change) => {
let settings = <Settings>change.settings;
maxNumberOfProblems = settings.li18nt.maxNumberOfProblems || 100;
// Revalidate any open text documents
documents.all().forEach(validateTextDocument);
});
function validateTextDocument(textDocument: TextDocument): void {
let diagnostics: Diagnostic[] = [];
let messages: SGStringProblem[] = [];
let lines = textDocument.getText().split(/\r?\n/g);
let problems = 0;
for (var i = 0; i < lines.length && problems < maxNumberOfProblems; i++) {
let line = lines[i];
rules.forEach(rule => {
let newProblems = rule(line, i);
messages.push(...newProblems);
problems += newProblems.length;
});
}
messages.forEach(message => {
let errorMessage = sgErrorMessages[message.messageCode] || "No message available";
let fullMessageText = `${message.messageCode}: ${errorMessage}`;
let diag: Diagnostic = {
severity: DiagnosticSeverity.Warning,
range: {
start: { line: message.lineNum, character: message.startCharPos },
end: { line: message.lineNum, character: message.endCharPos }
},
message: fullMessageText,
source: 'li18nt'
}
diagnostics.push(diag);
});
// Send the computed diagnostics to VSCode.
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}
connection.onDidChangeWatchedFiles((_change) => {
// Monitored files have change in VSCode
connection.console.log('We received an file change event');
});
// This handler provides the initial list of the completion items.
connection.onCompletion((_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
// The pass parameter contains the position of the text document in
// which code complete got requested. For the example we ignore this
// info and always provide the same completion items.
return [
{
label: 'TypeScript',
kind: CompletionItemKind.Text,
data: 1
},
{
label: 'JavaScript',
kind: CompletionItemKind.Text,
data: 2
}
]
});
// This handler resolve additional information for the item selected in
// the completion list.
connection.onCompletionResolve((item: CompletionItem): CompletionItem => {
if (item.data === 1) {
item.detail = 'TypeScript details',
item.documentation = 'TypeScript documentation'
} else if (item.data === 2) {
item.detail = 'JavaScript details',
item.documentation = 'JavaScript documentation'
}
return item;
});
/*
connection.onDidOpenTextDocument((params) => {
// A text document got opened in VSCode.
// params.uri uniquely identifies the document. For documents store on disk this is a file URI.
// params.text the initial full content of the document.
connection.console.log(`${params.textDocument.uri} opened.`);
});
connection.onDidChangeTextDocument((params) => {
// The content of a text document did change in VSCode.
// params.uri uniquely identifies the document.
// params.contentChanges describe the content changes to the document.
connection.console.log(`${params.textDocument.uri} changed: ${JSON.stringify(params.contentChanges)}`);
});
connection.onDidCloseTextDocument((params) => {
// A text document got closed in VSCode.
// params.uri uniquely identifies the document.
connection.console.log(`${params.textDocument.uri} closed.`);
});
*/
// Listen on the connection
connection.listen();
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"lib" : [ "es2016" ],
"outDir": "../client/server"
},
"exclude": [
"node_modules"
]
}