mirror of
https://github.com/foambubble/foam.git
synced 2026-01-11 06:58:11 -05:00
Compare commits
2 Commits
v0.19.2
...
spike/lsp-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2f46af479 | ||
|
|
b0051b4da3 |
19
.vscode/launch.json
vendored
19
.vscode/launch.json
vendored
@@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
42
docs/lsp-architecture.md
Normal file
42
docs/lsp-architecture.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# LSP Architecture
|
||||
|
||||
## Information requirements
|
||||
|
||||
### Document Completion
|
||||
|
||||
- List of all files in the workspace
|
||||
- Name
|
||||
- Title
|
||||
- Short preview of content
|
||||
|
||||
### Anchor completion
|
||||
|
||||
- List of all headings in the workspace
|
||||
- List of all paragraphs in the workspace
|
||||
- With full content
|
||||
- With context of where they are in the document
|
||||
- With hash ids
|
||||
- With document positions
|
||||
|
||||
## Link highlight
|
||||
|
||||
- All links
|
||||
- With positions
|
||||
|
||||
## Go to / peek link definition
|
||||
|
||||
- Get link at position
|
||||
- Get link target document
|
||||
- Get link target range
|
||||
|
||||
## Go to / peek references
|
||||
|
||||
- Get all links that point to file
|
||||
- Get all links that point to section/id
|
||||
- Get all instances of a hashtag
|
||||
|
||||
## Symbols
|
||||
|
||||
- Get all links in a document
|
||||
- Get all hashtags in a document
|
||||
- Get all block ids in a document
|
||||
33
packages/foam-language-server/package.json
Normal file
33
packages/foam-language-server/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"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": {
|
||||
"file-uri-to-path": "^2.0.0",
|
||||
"foam-core": "^0.3.0",
|
||||
"remark-stringify": "^8.1.1",
|
||||
"short-hash": "^1.0.0",
|
||||
"vscode-languageserver": "^6.1.1",
|
||||
"vscode-languageserver-textdocument": "^1.0.1"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts"
|
||||
}
|
||||
100
packages/foam-language-server/src/file.ts
Normal file
100
packages/foam-language-server/src/file.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import unified from 'unified';
|
||||
import markdownParse from 'remark-parse';
|
||||
import markdownStringify from 'remark-stringify';
|
||||
import wikiLinkPlugin from 'remark-wiki-link';
|
||||
import frontmatterPlugin from 'remark-frontmatter';
|
||||
import shortHash from 'short-hash';
|
||||
import visit from 'unist-util-visit';
|
||||
import { Node, Parent, Point, Position } from 'unist';
|
||||
import GitHubSlugger from 'github-slugger';
|
||||
let processor: unified.Processor | null = null;
|
||||
|
||||
let stringifier = unified()
|
||||
.use(markdownStringify)
|
||||
.use(wikiLinkPlugin);
|
||||
|
||||
function parse(markdown: string): Node {
|
||||
processor =
|
||||
processor ||
|
||||
unified()
|
||||
.use(markdownParse, { gfm: true })
|
||||
.use(frontmatterPlugin, ['yaml'])
|
||||
.use(wikiLinkPlugin);
|
||||
return processor.parse(markdown);
|
||||
}
|
||||
|
||||
export interface FileBlock {
|
||||
text: string;
|
||||
type: string;
|
||||
hash?: string;
|
||||
sort: string;
|
||||
key: string;
|
||||
position: Position;
|
||||
}
|
||||
|
||||
export function getFileBlocks(markdown: string): FileBlock[] {
|
||||
let tree = parse(markdown);
|
||||
let blocks: FileBlock[] = [];
|
||||
let order = 0;
|
||||
visit(tree, node => {
|
||||
const block = getFileBlockFromNode(node, ++order);
|
||||
if (block) {
|
||||
blocks.push(block);
|
||||
}
|
||||
});
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
let previouslySeenListItemLine = -1;
|
||||
function getFileBlockFromNode(node: Node, order: number): FileBlock | null {
|
||||
switch (node.type) {
|
||||
case 'heading': {
|
||||
const text = (node.children as any[])[0].value as string;
|
||||
return {
|
||||
text,
|
||||
key: new GitHubSlugger().slug(text),
|
||||
type: node.type,
|
||||
sort: order.toString().padStart(6, '0'),
|
||||
position: node.position!,
|
||||
};
|
||||
}
|
||||
|
||||
case 'listItem': {
|
||||
// @todo unhack
|
||||
previouslySeenListItemLine = node.position!.start.line;
|
||||
return null;
|
||||
}
|
||||
|
||||
case 'paragraph': {
|
||||
let text;
|
||||
let existingHash;
|
||||
let hashRegex = /\^(\w+)\s{0,}$/;
|
||||
|
||||
try {
|
||||
text = stringifier!.stringify(node);
|
||||
let matches = hashRegex.exec(text);
|
||||
if (matches) {
|
||||
existingHash = matches[1];
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Stringify failed', e.message, e, node);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const type = node.position!.start.line === previouslySeenListItemLine ? 'listItem' : 'paragraph';
|
||||
return {
|
||||
text,
|
||||
hash: existingHash,
|
||||
key: existingHash || shortHash(text),
|
||||
type,
|
||||
sort: order.toString().padStart(6, '0'),
|
||||
position: node.position!,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
423
packages/foam-language-server/src/index.ts
Normal file
423
packages/foam-language-server/src/index.ts
Normal file
@@ -0,0 +1,423 @@
|
||||
import {
|
||||
createConnection,
|
||||
ProposedFeatures,
|
||||
InitializeParams,
|
||||
DidChangeConfigurationNotification,
|
||||
CompletionItem,
|
||||
CompletionItemKind,
|
||||
TextDocuments,
|
||||
TextDocumentPositionParams,
|
||||
TextDocumentSyncKind,
|
||||
InsertTextFormat,
|
||||
InitializeResult,
|
||||
Position,
|
||||
TextDocumentContentChangeEvent,
|
||||
Range,
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||
import { getWorkspaceFiles } from './workspace';
|
||||
import { getFileBlocks, FileBlock } from './file';
|
||||
|
||||
const innerUpdate = TextDocument.update;
|
||||
TextDocument.update = (document: TextDocument, changes: TextDocumentContentChangeEvent[], version: number) => {
|
||||
const updated = innerUpdate(document, changes, version);
|
||||
siphonOnDidChangeTextDocumentEvent(document, changes);
|
||||
return updated;
|
||||
}
|
||||
|
||||
const textDocumentManager = new TextDocuments(TextDocument);
|
||||
|
||||
let workspaceFiles: {
|
||||
title: string;
|
||||
fileName: string;
|
||||
filePath: string;
|
||||
target: string;
|
||||
preview: string;
|
||||
}[] = [];
|
||||
|
||||
// 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.
|
||||
executeCommandProvider: {
|
||||
commands: ['foam/insertHash'],
|
||||
},
|
||||
completionProvider: {
|
||||
triggerCharacters: ['[', '#'],
|
||||
resolveProvider: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
result.capabilities.workspace = {
|
||||
workspaceFolders: {
|
||||
supported: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
connection.onInitialized(async () => {
|
||||
if (hasConfigurationCapability) {
|
||||
// Register for all configuration changes.
|
||||
connection.client.register(
|
||||
DidChangeConfigurationNotification.type,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
const folders = await connection.workspace.getWorkspaceFolders();
|
||||
if (!folders || folders.length === 0) {
|
||||
console.error('No workspace folders found');
|
||||
} else {
|
||||
if (folders.length > 1) {
|
||||
console.log('Multi-root workspaces not yet supported');
|
||||
}
|
||||
|
||||
let folder = folders[0];
|
||||
console.log('Initializing workspace at ' + folder.uri);
|
||||
workspaceFiles = await getWorkspaceFiles(folder.uri);
|
||||
}
|
||||
}
|
||||
|
||||
textDocumentManager.onDidOpen(e => {
|
||||
console.log('onDidOpen', e.document.uri);
|
||||
});
|
||||
|
||||
textDocumentManager.onDidClose(e => {
|
||||
// console.log('onDidClose', e.document);
|
||||
});
|
||||
|
||||
textDocumentManager.onDidChangeContent(e => {
|
||||
console.log('onDidChange', e.document.uri);
|
||||
});
|
||||
|
||||
textDocumentManager.onWillSave(e => {
|
||||
// console.log('onWillSave', e.document, e.reason);
|
||||
});
|
||||
|
||||
textDocumentManager.onWillSaveWaitUntil((e, cancellationToken) => {
|
||||
// console.log('onWillSaveTextDocumentWaitUntil', e.document, e.reason);
|
||||
// Could return text edits here;
|
||||
return [];
|
||||
});
|
||||
|
||||
textDocumentManager.onDidSave(e => {
|
||||
// console.log('onDidSave', e.document);
|
||||
});
|
||||
});
|
||||
|
||||
connection.onDidChangeConfiguration(change => {
|
||||
if (hasConfigurationCapability) {
|
||||
// todo
|
||||
}
|
||||
});
|
||||
|
||||
connection.onDidChangeWatchedFiles(_change => {
|
||||
// Monitored files have change in VSCode
|
||||
connection.console.log('We received an file change event');
|
||||
});
|
||||
|
||||
connection.onExecuteCommand(async e => {
|
||||
if (e.command === 'foam/insertHash') {
|
||||
const {
|
||||
sourceUri,
|
||||
sourcePosition,
|
||||
targetUri,
|
||||
targetBlock,
|
||||
}: {
|
||||
sourceUri: string;
|
||||
sourcePosition: Position;
|
||||
targetUri: string;
|
||||
targetBlock: FileBlock;
|
||||
} = e.arguments![0];
|
||||
|
||||
const astPosition = targetBlock.position;
|
||||
const docPosition = {
|
||||
line: astPosition.end.line - 1,
|
||||
character: astPosition.end.column - 1,
|
||||
};
|
||||
|
||||
const result = await connection.workspace.applyEdit({
|
||||
label: 'Insert generated hash',
|
||||
edit: {
|
||||
changes: {
|
||||
[targetUri]: [
|
||||
{
|
||||
newText: ` ^${targetBlock.key}`,
|
||||
range: {
|
||||
start: docPosition,
|
||||
end: docPosition,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (result.applied) {
|
||||
activeHashEdit = {
|
||||
sourceUri: sourceUri,
|
||||
sourceRange: {
|
||||
start: sourcePosition,
|
||||
end: {
|
||||
...sourcePosition,
|
||||
character: sourcePosition.character + targetBlock.key.length,
|
||||
},
|
||||
},
|
||||
targetUri: targetUri,
|
||||
targetRange: {
|
||||
start: { ...docPosition, character: docPosition.character },
|
||||
end: {
|
||||
...docPosition,
|
||||
character: docPosition.character + targetBlock.key.length + 2, // +" ^"
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
console.log(
|
||||
'foam/insertHash',
|
||||
sourceUri,
|
||||
sourcePosition,
|
||||
targetUri,
|
||||
targetBlock
|
||||
);
|
||||
} else {
|
||||
console.log('unrecognized command', e.command, e.arguments);
|
||||
}
|
||||
});
|
||||
|
||||
function getAvailableSections(
|
||||
uri: string,
|
||||
position: Position
|
||||
): CompletionItem[] {
|
||||
const targetUri =
|
||||
'file:///Users/swanson/dev/foam-link-completion-demo/Principles.md';
|
||||
const document = textDocumentManager.get(targetUri);
|
||||
const blocks = getFileBlocks(document!.getText());
|
||||
|
||||
// try to find the end brackets, so we can remove them and add them
|
||||
// back on the other side of our cursor, so when the insertion is
|
||||
// completed, the text cursor will remain on the outside of the brackets
|
||||
|
||||
// const sections = getMockSections();
|
||||
// const document = textDocumentManager.get(uri);
|
||||
// if (document) {
|
||||
// const endBracketRange = {
|
||||
// start: position,
|
||||
// end: { ...position, character: position.character + 2 },
|
||||
// };
|
||||
|
||||
// if (document.getText(endBracketRange) == ']]') {
|
||||
// return sections.map((section) => ({
|
||||
// label: `#${section}`,
|
||||
// kind: CompletionItemKind.Text,
|
||||
// insertTextFormat: InsertTextFormat.Snippet,
|
||||
// textEdit: {
|
||||
// newText: '${1:' + shortHash(section) + '}]] $0',
|
||||
// range: endBracketRange
|
||||
// },
|
||||
// }));
|
||||
// }
|
||||
// }
|
||||
|
||||
const kindsByType: { [x: string]: CompletionItemKind } = {
|
||||
heading: CompletionItemKind.Folder,
|
||||
paragraph: CompletionItemKind.Text,
|
||||
listItem: CompletionItemKind.Keyword,
|
||||
};
|
||||
|
||||
// no luck locating end brocket range, just inject the value
|
||||
// and let user handle in manually
|
||||
return blocks.map(block => {
|
||||
const labelPrefix = block.type === 'heading' ? '# ' : '';
|
||||
const insertNewHash = block.type !== 'heading' && !block.hash;
|
||||
return {
|
||||
label: `${labelPrefix}${block.text}`,
|
||||
detail: block.key,
|
||||
documentation: {
|
||||
kind: 'markdown',
|
||||
value: block.text,
|
||||
},
|
||||
kind: kindsByType[block.type],
|
||||
sortText: block.sort.toString(),
|
||||
filterText: block.text,
|
||||
insertText: insertNewHash ? '${1:' + block.key + '}$0' : `${block.key}$0`,
|
||||
insertTextFormat: InsertTextFormat.Snippet,
|
||||
command: insertNewHash
|
||||
? {
|
||||
title: 'Insert Hash',
|
||||
command: 'foam/insertHash',
|
||||
arguments: [
|
||||
{
|
||||
sourceUri: uri,
|
||||
sourcePosition: position,
|
||||
targetUri: targetUri,
|
||||
targetBlock: block,
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function isRequestAtWikiLinkStartBlock(
|
||||
uri: string,
|
||||
position: Position
|
||||
): boolean {
|
||||
|
||||
// find the currently open document
|
||||
const document = textDocumentManager.get(uri);
|
||||
if (!document) {
|
||||
console.log('document not found by uri', uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the preceding three character range
|
||||
const text = document.getText({
|
||||
start: { line: position.line, character: position.character - 3 },
|
||||
end: position,
|
||||
});
|
||||
|
||||
// we get three characters, so that we can trigger autocompletion in following cases:
|
||||
// 1. when user types the second "[", the range is " [[", and we show all files
|
||||
// 2. when user has cleared existing link content and types the first character
|
||||
// of the new content, e.g. "[[S".
|
||||
return text.includes('[[');
|
||||
}
|
||||
|
||||
// This handler provides the initial list of the completion items.
|
||||
connection.onCompletion(
|
||||
(_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
|
||||
console.log('onCompletion.textDocumentPosition', _textDocumentPosition);
|
||||
|
||||
const documentContext = (_textDocumentPosition as any).context;
|
||||
if (documentContext && documentContext.triggerCharacter === '#') {
|
||||
return getAvailableSections(
|
||||
_textDocumentPosition.textDocument.uri,
|
||||
_textDocumentPosition.position
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
isRequestAtWikiLinkStartBlock(
|
||||
_textDocumentPosition.textDocument.uri,
|
||||
_textDocumentPosition.position
|
||||
)
|
||||
) {
|
||||
return workspaceFiles.map(document => ({
|
||||
label: document.title,
|
||||
detail: document.fileName,
|
||||
documentation: document.preview,
|
||||
commitCharacters: ['#', '|'],
|
||||
kind: CompletionItemKind.Text,
|
||||
insertTextFormat: InsertTextFormat.Snippet,
|
||||
textEdit: {
|
||||
newText: document.target + '$1]] $0',
|
||||
range: {
|
||||
start: _textDocumentPosition.position,
|
||||
end: {
|
||||
..._textDocumentPosition.position,
|
||||
character: _textDocumentPosition.position.character + 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
);
|
||||
|
||||
connection.onCompletionResolve(item => {
|
||||
return item;
|
||||
});
|
||||
|
||||
textDocumentManager.listen(connection);
|
||||
|
||||
// Listen on the connection
|
||||
connection.listen();
|
||||
|
||||
|
||||
let activeHashEdit: {
|
||||
sourceUri: string;
|
||||
sourceRange: Range;
|
||||
targetUri: string;
|
||||
targetRange: Range;
|
||||
} | null = null;
|
||||
|
||||
|
||||
function siphonOnDidChangeTextDocumentEvent(document: TextDocument, changes: TextDocumentContentChangeEvent[]) {
|
||||
if (!activeHashEdit) return;
|
||||
if (document.uri !== activeHashEdit.sourceUri) return;
|
||||
if (changes.length !== 1) return;
|
||||
|
||||
const edit = activeHashEdit;
|
||||
const text = changes[0].text;
|
||||
const range = (changes[0] as any).range as Range;
|
||||
|
||||
if (!range) return;
|
||||
|
||||
if (
|
||||
range.start.line === range.end.line &&
|
||||
range.start.line === edit.sourceRange.start.line &&
|
||||
range.end.line === edit.sourceRange.end.line &&
|
||||
range.start.character >= edit.sourceRange.start.character &&
|
||||
range.end.character <= edit.sourceRange.end.character + 1
|
||||
) {
|
||||
const val = document.getText(edit.sourceRange);
|
||||
const hash = val.split(']')[0];
|
||||
|
||||
console.log('Text at edit point', hash);
|
||||
|
||||
if (range.end.character == edit.sourceRange.end.character + 1) {
|
||||
edit.sourceRange.end.character += 1;
|
||||
edit.targetRange.end.character += 1;
|
||||
activeHashEdit = edit;
|
||||
}
|
||||
|
||||
connection.workspace.applyEdit({
|
||||
label: 'Update generated hash',
|
||||
edit: {
|
||||
changes: {
|
||||
[edit.targetUri]: [
|
||||
{
|
||||
newText: hash.length === 0 ? '' : ` ^${hash}`,
|
||||
range: edit.targetRange,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
activeHashEdit = null;
|
||||
}
|
||||
};
|
||||
23
packages/foam-language-server/src/workspace.ts
Normal file
23
packages/foam-language-server/src/workspace.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import glob from 'glob';
|
||||
import uriToPath from 'file-uri-to-path';
|
||||
import { promisify } from 'util';
|
||||
import { extname, basename, relative } from 'path';
|
||||
|
||||
const findAllFiles = promisify(glob);
|
||||
|
||||
export async function getWorkspaceFiles(workspaceUri: string) {
|
||||
let path = uriToPath(workspaceUri);
|
||||
if (path.substr(-1) === '/') {
|
||||
path = path.slice(0, -1);
|
||||
}
|
||||
|
||||
const files = await findAllFiles(`${path}/**/*.md`, {});
|
||||
|
||||
return files.map(filePath => {
|
||||
const title = basename(filePath, extname(filePath));
|
||||
const fileName = relative(path, filePath);
|
||||
const ext = extname(filePath);
|
||||
const target = fileName.slice(0, -ext.length);
|
||||
return { title, filePath, fileName, target, preview: ''};
|
||||
})
|
||||
}
|
||||
8
packages/foam-language-server/tsconfig.build.json
Normal file
8
packages/foam-language-server/tsconfig.build.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "es6",
|
||||
"sourceMap": true,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
31
packages/foam-language-server/tsconfig.json
Normal file
31
packages/foam-language-server/tsconfig.json
Normal 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", "types"],
|
||||
"exclude": ["node_modules"],
|
||||
"references": [{
|
||||
"path": "../foam-core"
|
||||
}]
|
||||
}
|
||||
1
packages/foam-language-server/types/remark-wiki-link.d.ts
vendored
Normal file
1
packages/foam-language-server/types/remark-wiki-link.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module 'remark-wiki-link';
|
||||
4
packages/foam-language-server/types/short-hash.d.ts
vendored
Normal file
4
packages/foam-language-server/types/short-hash.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
declare module 'short-hash' {
|
||||
function shortHash(input: string): string;
|
||||
export = shortHash;
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
3
packages/foam-vscode/src/types.d.ts
vendored
3
packages/foam-vscode/src/types.d.ts
vendored
@@ -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>
|
||||
}
|
||||
|
||||
@@ -12,5 +12,7 @@
|
||||
"exclude": ["node_modules", ".vscode-test"],
|
||||
"references": [{
|
||||
"path": "../foam-core"
|
||||
}, {
|
||||
"path": "../foam-language-server"
|
||||
}]
|
||||
}
|
||||
|
||||
119
yarn.lock
119
yarn.lock
@@ -3923,6 +3923,11 @@ char-regex@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
|
||||
integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
|
||||
|
||||
character-entities-html4@^1.0.0:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125"
|
||||
integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==
|
||||
|
||||
character-entities-legacy@^1.0.0:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1"
|
||||
@@ -5540,6 +5545,11 @@ file-uri-to-path@1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
|
||||
file-uri-to-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba"
|
||||
integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==
|
||||
|
||||
fill-range@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
|
||||
@@ -6119,6 +6129,11 @@ has@^1.0.3:
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hash-string@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hash-string/-/hash-string-1.0.0.tgz#c3fa15f078ddd16bc150b4176fde7091620f2c7f"
|
||||
integrity sha1-w/oV8Hjd0WvBULQXb95wkWIPLH8=
|
||||
|
||||
hosted-git-info@^2.1.4, hosted-git-info@^2.7.1:
|
||||
version "2.8.8"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
||||
@@ -6448,6 +6463,11 @@ is-alphabetical@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d"
|
||||
integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==
|
||||
|
||||
is-alphanumeric@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4"
|
||||
integrity sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=
|
||||
|
||||
is-alphanumerical@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf"
|
||||
@@ -6502,7 +6522,7 @@ is-date-object@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
||||
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
||||
|
||||
is-decimal@^1.0.0:
|
||||
is-decimal@^1.0.0, is-decimal@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5"
|
||||
integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==
|
||||
@@ -8399,6 +8419,11 @@ log-update@^2.3.0:
|
||||
cli-cursor "^2.0.0"
|
||||
wrap-ansi "^3.0.1"
|
||||
|
||||
longest-streak@^2.0.1:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4"
|
||||
integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
@@ -8521,6 +8546,20 @@ markdown-escapes@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
|
||||
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
|
||||
|
||||
markdown-table@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b"
|
||||
integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==
|
||||
dependencies:
|
||||
repeat-string "^1.0.0"
|
||||
|
||||
mdast-util-compact@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz#cabc69a2f43103628326f35b1acf735d55c99490"
|
||||
integrity sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA==
|
||||
dependencies:
|
||||
unist-util-visit "^2.0.0"
|
||||
|
||||
meow@^3.3.0:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
|
||||
@@ -10109,6 +10148,26 @@ remark-parse@^8.0.2:
|
||||
vfile-location "^3.0.0"
|
||||
xtend "^4.0.1"
|
||||
|
||||
remark-stringify@^8.1.1:
|
||||
version "8.1.1"
|
||||
resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-8.1.1.tgz#e2a9dc7a7bf44e46a155ec78996db896780d8ce5"
|
||||
integrity sha512-q4EyPZT3PcA3Eq7vPpT6bIdokXzFGp9i85igjmhRyXWmPs0Y6/d2FYwUNotKAWyLch7g0ASZJn/KHHcHZQ163A==
|
||||
dependencies:
|
||||
ccount "^1.0.0"
|
||||
is-alphanumeric "^1.0.0"
|
||||
is-decimal "^1.0.0"
|
||||
is-whitespace-character "^1.0.0"
|
||||
longest-streak "^2.0.1"
|
||||
markdown-escapes "^1.0.0"
|
||||
markdown-table "^2.0.0"
|
||||
mdast-util-compact "^2.0.0"
|
||||
parse-entities "^2.0.0"
|
||||
repeat-string "^1.5.4"
|
||||
state-toggle "^1.0.0"
|
||||
stringify-entities "^3.0.0"
|
||||
unherit "^1.0.4"
|
||||
xtend "^4.0.1"
|
||||
|
||||
remark-wiki-link@^0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/remark-wiki-link/-/remark-wiki-link-0.0.4.tgz#9af79538714280513f209661794083d5ae3006d7"
|
||||
@@ -10127,7 +10186,7 @@ repeat-element@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
|
||||
integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
|
||||
|
||||
repeat-string@^1.5.4, repeat-string@^1.6.1:
|
||||
repeat-string@^1.0.0, repeat-string@^1.5.4, repeat-string@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
|
||||
@@ -10535,6 +10594,13 @@ shellwords@^0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
||||
|
||||
short-hash@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/short-hash/-/short-hash-1.0.0.tgz#3f491d728fcc777ec605bbaf7f83f23712f42050"
|
||||
integrity sha1-P0kdco/Md37GBbuvf4PyNxL0IFA=
|
||||
dependencies:
|
||||
hash-string "^1.0.0"
|
||||
|
||||
side-channel@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947"
|
||||
@@ -10904,6 +10970,17 @@ string_decoder@~1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
stringify-entities@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-3.0.1.tgz#32154b91286ab0869ab2c07696223bd23b6dbfc0"
|
||||
integrity sha512-Lsk3ISA2++eJYqBMPKcr/8eby1I6L0gP0NlxF8Zja6c05yr/yCYyb2c9PwXjd08Ib3If1vn1rbs1H5ZtVuOfvQ==
|
||||
dependencies:
|
||||
character-entities-html4 "^1.0.0"
|
||||
character-entities-legacy "^1.0.0"
|
||||
is-alphanumerical "^1.0.0"
|
||||
is-decimal "^1.0.2"
|
||||
is-hexadecimal "^1.0.0"
|
||||
|
||||
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
|
||||
@@ -11815,6 +11892,44 @@ 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-textdocument@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f"
|
||||
integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==
|
||||
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user