Setup Language Server boilerplate (#258)

This commit is contained in:
Jani Eväkallio
2020-09-10 10:49:02 +01:00
committed by GitHub
parent b2449df390
commit b0051b4da3
12 changed files with 337 additions and 7 deletions

19
.vscode/launch.json vendored
View File

@@ -71,6 +71,25 @@
"cwd": "${workspaceFolder}/packages/foam-core",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "${defaultBuildTask}"
},
{
"name": "Debug Language Server",
"type": "node",
"request": "attach",
"port": 6009,
// large timeout to account for typescript watch startup time
"timeout": 30000,
"restart": true,
"trace": true,
"localRoot": "${workspaceFolder}/packages/foam-language-server/src/",
"remoteRoot": "${workspaceFolder}/packages/foam-language-server/dist/",
"outFiles": ["${workspaceFolder}/packages/foam-language-server/dist/**/*.js"]
},
],
"compounds": [
{
"name": "Run Extension & Debug LSP",
"configurations": ["Run VSCode Extension", "Debug Language Server"]
}
]
}

View File

@@ -0,0 +1,29 @@
{
"name": "foam-language-server",
"author": "Jani Eväkallio",
"repository": "https://github.com/foambubble/foam",
"version": "0.3.0",
"license": "MIT",
"files": [
"dist"
],
"scripts": {
"clean": "rimraf dist",
"build": "tsdx build --tsconfig ./tsconfig.build.json",
"test": "tsdx test",
"lint": "tsdx lint src test",
"watch": "tsdx watch",
"prepare": "npm run build"
},
"devDependencies": {
"tsdx": "^0.13.2",
"tslib": "^2.0.0",
"typescript": "^3.9.5"
},
"dependencies": {
"foam-core": "^0.3.0",
"vscode-languageserver": "^6.1.1"
},
"main": "dist/index.js",
"types": "dist/index.d.ts"
}

View File

@@ -0,0 +1,125 @@
import {
createConnection,
ProposedFeatures,
InitializeParams,
DidChangeConfigurationNotification,
CompletionItem,
CompletionItemKind,
TextDocumentPositionParams,
TextDocumentSyncKind,
InitializeResult
} from 'vscode-languageserver';
const data = [
{
text: 'Foam',
displayName: 'Foam',
description: 'Foam is a personal knowledge management and sharing system built on Visual Studio Code and GitHub.'
},
{
text: 'Roam',
displayName: 'Roam Research',
description: 'Roam is a A note-taking tool for networked thought',
},
{
text: 'Obsidian',
displayName: "Obsidian",
description: 'Obsidian is a powerful knowledge base that works on top of a local folder of plain text Markdown files.'
}
];
// Create a connection for the server, using Node's IPC as a transport.
// Also include all preview / proposed LSP features.
let connection = createConnection(ProposedFeatures.all);
let hasConfigurationCapability: boolean = false;
let hasWorkspaceFolderCapability: boolean = false;
connection.onInitialize((params: InitializeParams) => {
let capabilities = params.capabilities;
// Does the client support the `workspace/configuration` request?
// If not, we fall back using global settings.
hasConfigurationCapability = !!(
capabilities.workspace && !!capabilities.workspace.configuration
);
hasWorkspaceFolderCapability = !!(
capabilities.workspace && !!capabilities.workspace.workspaceFolders
);
const result: InitializeResult = {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Incremental,
// Tell the client that this server supports code completion.
completionProvider: {
triggerCharacters: ["["],
resolveProvider: true
}
}
};
if (hasWorkspaceFolderCapability) {
result.capabilities.workspace = {
workspaceFolders: {
supported: true
}
};
}
return result;
});
connection.onInitialized(() => {
if (hasConfigurationCapability) {
// Register for all configuration changes.
connection.client.register(DidChangeConfigurationNotification.type, undefined);
}
if (hasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders(_event => {
connection.console.log('Workspace folder change event received.');
});
}
});
connection.onDidChangeConfiguration(change => {
if (hasConfigurationCapability) {
}
});
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[] => {
console.log('onCompletion.textDocumentPosition', _textDocumentPosition);
// 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 data.map(item => ({
label: item.displayName,
kind: CompletionItemKind.Text,
data: item.text
}));
}
);
// This handler resolves additional information for the item selected in
// the completion list.
connection.onCompletionResolve(
(item: CompletionItem): CompletionItem => {
console.log('onCompletionResolve.item', item);
const product = data.find(p => p.text = item.data);
if (product) {
item.detail = product.displayName;
item.documentation = product.description;
}
return item;
}
);
// Listen on the connection
connection.listen();

View File

@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "es6",
"sourceMap": true,
"inlineSourceMap": false
}
}

View File

@@ -0,0 +1,31 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": "src",
"composite": true,
"esModuleInterop": true,
"importHelpers": true,
"module": "CommonJS",
"moduleResolution": "node",
"outDir": "dist",
"rootDir": "./src",
"strict": true,
"lib": [
"esnext"
],
// @NOTE regarding inlineSourceMap:
//
// Preferrably we would use "sourceMap": true to generate
// *.map.js files instead of inline base64 encoded source maps,
// however for some reason the VSCode Node debugger looks
// for source maps in the src/ directory instead of dist/ directory,
// causing the VSCode debugging experience to break.
"inlineSourceMap": true,
},
"include": ["src"],
"exclude": ["node_modules"],
"references": [{
"path": "../foam-core"
}]
}

View File

@@ -91,8 +91,8 @@
"build": "tsc -p ./",
"test": "jest",
"lint": "eslint src --ext ts",
"clean": "tsc --build ./tsconfig.json ../foam-core/tsconfig.json --clean",
"watch": "tsc --build ./tsconfig.json ../foam-core/tsconfig.json --watch",
"clean": "tsc --build ./tsconfig.json ../foam-core/tsconfig.json ../foam-language-server/tsconfig.json --clean",
"watch": "tsc --build ./tsconfig.json ../foam-core/tsconfig.json ../foam-language-server/tsconfig.json --watch",
"vscode:start-debugging": "yarn clean && yarn watch",
"vscode:prepublish": "yarn npm-install && yarn run build",
"npm-install": "rimraf node_modules && npm i",
@@ -120,6 +120,7 @@
},
"dependencies": {
"dateformat": "^3.0.3",
"foam-core": "0.3.0"
"foam-core": "0.3.0",
"vscode-languageclient": "^6.1.3"
}
}

View File

@@ -32,8 +32,14 @@ export function activate(context: ExtensionContext) {
});
}
export function deactivate() {
export async function deactivate(): Promise<void> {
workspaceWatcher?.dispose();
// call all deactivate functions and if any of them
// return promises, wait for their completion
await Promise.all(features.map(
f => (f.deactivate && f.deactivate()) || Promise.resolve()
));
}
function isLocalMarkdownFile(uri: Uri) {
@@ -69,7 +75,7 @@ const bootstrap = async (config: FoamConfig) => {
true,
true
);
workspaceWatcher.onDidCreate(uri => {
if (isLocalMarkdownFile(uri)) {
addFile(uri).then(() => {

View File

@@ -1,6 +1,7 @@
import createReferences from "./wikilink-reference-generation";
import openDailyNote from "./open-daily-note";
import janitor from "./janitor";
import languageServerClient from "./language-server-client";
import { FoamFeature } from "../types";
export const features: FoamFeature[] = [createReferences, openDailyNote, janitor];
export const features: FoamFeature[] = [createReferences, languageServerClient, openDailyNote, janitor];

View File

@@ -0,0 +1,74 @@
import * as path from "path";
import { workspace, ExtensionContext } from "vscode";
import { FoamFeature } from "../../types";
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
TransportKind
} from "vscode-languageclient";
let client: LanguageClient;
const feature: FoamFeature = {
activate: (context: ExtensionContext) => {
// @TODO figure out path for production deploys
let serverModule = context.asAbsolutePath(
path.join("..", "foam-language-server", "dist", "index.js")
);
// The debug options for the server
// --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
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 markdown documents
documentSelector: [
{ scheme: "file", language: "markdown" },
{ scheme: "file", language: "mdx" }
],
synchronize: {
fileEvents: workspace.createFileSystemWatcher(
"**/.vscode/settings.json"
)
}
};
// Create the language client and start the client.
client = new LanguageClient(
"foam-language-server",
"Foam Language Server",
serverOptions,
clientOptions
);
console.log(
"Starting foam-language-server with options\n",
JSON.stringify(serverOptions, null, 2)
);
// Start the client. This will also launch the server
client.start();
},
deactivate() {
if (client) {
console.log("Stopping foam-language-server");
return client.stop();
}
}
};
export default feature;

View File

@@ -2,5 +2,6 @@ import { ExtensionContext } from "vscode";
import { Foam } from "foam-core";
export interface FoamFeature {
activate: (context: ExtensionContext, foamPromise: Promise<Foam>) => void
activate: (context: ExtensionContext, foamPromise: Promise<Foam>) => void,
deactivate?: () => Promise<void>
}

View File

@@ -12,5 +12,7 @@
"exclude": ["node_modules", ".vscode-test"],
"references": [{
"path": "../foam-core"
}, {
"path": "../foam-language-server"
}]
}

View File

@@ -11815,6 +11815,39 @@ vfile@^4.0.0:
unist-util-stringify-position "^2.0.0"
vfile-message "^2.0.0"
vscode-jsonrpc@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794"
integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==
vscode-languageclient@^6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz#c979c5bb5855714a0307e998c18ca827c1b3953a"
integrity sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==
dependencies:
semver "^6.3.0"
vscode-languageserver-protocol "^3.15.3"
vscode-languageserver-protocol@^3.15.3:
version "3.15.3"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb"
integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==
dependencies:
vscode-jsonrpc "^5.0.1"
vscode-languageserver-types "3.15.1"
vscode-languageserver-types@3.15.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"
integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
vscode-languageserver@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz#d76afc68172c27d4327ee74332b468fbc740d762"
integrity sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ==
dependencies:
vscode-languageserver-protocol "^3.15.3"
vscode-test@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-1.4.0.tgz#a56f73c1667b4d37ba6baa6765f233a19d4ffbfe"