Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Language server #117

Draft
wants to merge 37 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6a6967c
Merge branch 'master' of https://github.com/BarryNolte/d2-vscode
BarryNolte Jun 16, 2023
a35eeac
Work In Progress
BarryNolte Jun 28, 2023
86faaa8
Bootstraping Language Server
BarryNolte Jul 7, 2023
8268aea
Change fixed path to path from settings file
BarryNolte Jul 8, 2023
4935810
Fixed up d2 path, again
BarryNolte Jul 8, 2023
689a24a
Checkpoint - File Import Completion Working
BarryNolte Jul 13, 2023
9958431
Checkpoint - File Import Completion Working
BarryNolte Jul 13, 2023
eddc7f7
CheckPoint
BarryNolte Jul 15, 2023
355fb28
Checkpoint
BarryNolte Jul 23, 2023
f5af735
Reconfigured directory structure
BarryNolte Jul 24, 2023
c8fb692
Checkpoint
BarryNolte Jul 25, 2023
74118df
Pre-Code Cleanup
BarryNolte Jul 29, 2023
6eaea67
Merge branch 'terrastruct:master' into LanguageServer
BarryNolte Aug 2, 2023
0a7c2c0
First Language Server PR
BarryNolte Aug 2, 2023
06899b9
Merge branch 'LanguageServer' of https://github.com/BarryNolte/d2-vsc…
BarryNolte Aug 2, 2023
6594075
Final push before pull request
BarryNolte Aug 8, 2023
7df1372
Get CI to work (maybe...)
BarryNolte Aug 10, 2023
313251f
Try again to get ci to work
BarryNolte Aug 10, 2023
748398b
Code Review Fixes
BarryNolte Nov 3, 2023
ccbe41f
Code review updates and general tweaks and fixes.
BarryNolte Nov 8, 2023
6b9dc75
Change prettier version to work with CI
BarryNolte Nov 8, 2023
9a20650
Reworked autocomplete, file linking and markdown embedding.
BarryNolte Dec 30, 2023
d8eaeee
Code clean-up
BarryNolte Dec 30, 2023
6b86fc1
Code Cleanup
BarryNolte Jan 1, 2024
ec25e38
Merge branch 'master' into LanguageServer
BarryNolte Jan 2, 2024
02fe37b
Fix formatting bugs
BarryNolte Jan 2, 2024
aeff708
Merge branch 'LanguageServer' of https://github.com/BarryNolte/d2-vsc…
BarryNolte Jan 2, 2024
c254965
eslint version mismatch
BarryNolte Jan 2, 2024
4f0ff39
Set eslint to specific version
BarryNolte Jan 2, 2024
7c95cc4
Fix for package generation
BarryNolte Feb 7, 2024
1976c65
Code review changes
BarryNolte Feb 8, 2024
909980b
Format and forgotten file
BarryNolte Feb 8, 2024
6d5aca9
Code Review Change
BarryNolte Feb 16, 2024
ed206a0
Change request was not the answer we were looking for.
BarryNolte Feb 16, 2024
b0f75cd
Work In Progress
BarryNolte Mar 21, 2024
86f1055
Update dependencies
BarryNolte Mar 30, 2024
99bbb27
Add a 'recompile' button
BarryNolte Mar 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module",
"project": ["src/tsconfig.json"]
"project": ["server/tsconfig.json", "client/tsconfig.json"]
},
"plugins": ["@typescript-eslint"],
"env": {
"browser": false,
"es2021": true
"es2022": true
},
"extends": [
"eslint:recommended",
Expand Down Expand Up @@ -169,7 +169,7 @@
"no-script-url": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow": "error",
"@typescript-eslint/no-shadow": "error",
"no-shadow-restricted-names": "error",
"no-spaced-func": "error",
"no-sync": "error",
Expand All @@ -184,7 +184,7 @@
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
"no-unused-expressions": "error",
"no-unused-vars": "error",
"@typescript-eslint/no-unused-vars": "error",
"no-use-before-define": "off",
"no-useless-call": "error",
"no-useless-computed-key": "error",
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ dist
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.vscode/settings.json
package-lock.json
24 changes: 19 additions & 5 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
{
"version": "0.2.0",

"compounds": [
{
"name": "Server/Client",
"configurations": ["Attach to Server", "Launch Extension"],
"stopAll": true
}
],
"configurations": [
{
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"name": "Attach to Server",
"port": 6009,
"request": "attach",
"skipFiles": ["<node_internals>/**"],
"type": "node"
},
{
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"name": "Launch Extension",
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
"${workspaceFolder}/client/dist/**/*.js",
"${workspaceFolder}/server/dist/**/*.js"
],
"request": "launch",
"type": "extensionHost"
}
]
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ To package and install the extension locally, run:

```sh
npm install -g @vscode/vsce
yarn installdep
npm run dev
```

Expand Down
51 changes: 51 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "d2-extension-client",
"displayName": "D2",
"publisher": "Terrastruct",
"description": "Support for .d2 files.",
"version": "0.8.7",
"license": "BSD-3-Clause",
"contributors": [
"Barry Nolte <barry@barrynolte.com>"
],
"engines": {
"vscode": "^1.73.0"
},
"devDependencies": {
"@types/glob": "^8.1.0",
"@types/markdown-it-container": "^2.0.9",
"@types/mocha": "^10.0.3",
"@types/node": "^20.8.10",
"@types/vscode": "^1.84.0",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"@vscode/test-electron": "^2.3.6",
"@vscode/vsce": "^2.22.0",
"eslint": "8.36.0",
"eslint-config-prettier": "^8.6.0",
"eslint-formatter-pretty": "^4.1.0",
"eslint-webpack-plugin": "^4.0.1",
"glob": "^10.3.10",
"mocha": "^10.2.0",
"prettier": "^2.8.3",
"ts-loader": "^9.5.0",
"typescript": "^5.2.2",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"markdown-it-container": "^4.0.0",
"path": "^0.12.7",
"vscode-languageclient": "^9.0.1"
},
"homepage": "https://d2lang.com",
"icon": "d2-icon.png",
"bugs": {
"url": "https://github.com/terrastruct/d2-vscode/issues",
"email": "info@terrastruct.com"
},
"repository": {
"type": "git",
"url": "https://github.com/terrastruct/d2-vscode"
}
}
3 changes: 3 additions & 0 deletions src/browserWindow.ts → client/src/browserWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export class BrowserWindow {
localResourceRoots: [Uri.file(path.join(extContext.extensionPath, "pages"))],
}
);
this.webViewPanel.iconPath = Uri.file(
path.join(extContext.extensionPath, "d2-icon-small.png")
);
this.webView = this.webViewPanel.webview;

const onDiskPath = path.join(extContext.extensionPath, "pages/previewPage.html");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class DocToPreviewGenerator {
trkObj.outputDoc.showBusy();

taskRunner.genTask(trkObj.inputDoc?.fileName, fileText, (data, error) => {
const p = path.parse(trkObj.inputDoc?.fileName || "");
const p = path.parse(trkObj.inputDoc?.fileName ?? "");

if (data.length > 0) {
trkObj.outputDoc?.setSvg(data);
Expand All @@ -89,7 +89,17 @@ export class DocToPreviewGenerator {

let list = "";
arr.forEach((s) => {
list += `<li>${s}</li>`;
if (s.length > 0) {
/*
Matches: "error:0:0: message" or
"warning:100:20:explaination"
*/
const rg = new RegExp(/^(.*?):(\d+):(\d+):(\s+)(.*)$/g).exec(s);
BarryNolte marked this conversation as resolved.
Show resolved Hide resolved
const msg = rg !== null ? rg[5] : s;
const line = rg ? rg[2] ?? "0" : "0";
const col = rg ? rg[3] ?? "0" : "0";
list += `<li>${msg} (${line}:${col})</li>`;
}
});

trkObj.outputDoc?.setToastMsg("Errors");
Expand Down
98 changes: 84 additions & 14 deletions src/extension.ts → client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,26 @@ import {

import { DocToPreviewGenerator } from "./docToPreviewGenerator";
import { D2OutputChannel } from "./outputChannel";
import * as mdItContainer from "markdown-it-container";
import { layoutPicker } from "./layoutPicker";
import { themePicker } from "./themePicker";
import { TaskRunner } from "./taskRunner";
import { d2Tasks } from "./tasks";
import { util } from "./utility";
import path = require("path");
import { TextEncoder } from "util";
import markdownContainer = require("markdown-it-container");
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
TransportKind,
} from "vscode-languageclient/node";

const d2Ext = "d2";
const d2Lang = "d2";
const pluginKeyword = "d2";
const previewGenerator: DocToPreviewGenerator = new DocToPreviewGenerator();
let langClient: LanguageClient;

export const d2ConfigSection = "D2";
export let ws: WorkspaceConfiguration = workspace.getConfiguration(d2ConfigSection);
Expand All @@ -39,7 +47,9 @@ export const taskRunner: TaskRunner = new TaskRunner();
export let extContext: ExtensionContext;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function activate(context: ExtensionContext): any {
export type ExtAny = any;

export function activate(context: ExtensionContext): ExtAny {
extContext = context;

context.subscriptions.push(
Expand Down Expand Up @@ -236,40 +246,100 @@ export function activate(context: ExtensionContext): any {
util.checkForD2Install();
}

startLanguageServer();

// Return our markdown renderer
return {
// Sets up our ability to render for markdown files
// eslint-disable-next-line @typescript-eslint/no-explicit-any
extendMarkdownIt(md: any) {
return extendMarkdownItWithD2(md);
extendMarkdownIt(md: markdownit) {
extendMarkdownItWithD2(md);
return md;
},
};
}

const pluginKeyword = "d2";
/**
* This starts the D2 Language Server
*/
function startLanguageServer(): void {
// The server is implemented in node
const serverModule = extContext.asAbsolutePath(
path.join("server", "dist", "server.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
const 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
const serverOptions: ServerOptions = {
run: {
module: serverModule,
transport: TransportKind.ipc,
},
debug: {
module: serverModule,
transport: TransportKind.ipc,
options: debugOptions,
},
};

const clientOptions: LanguageClientOptions = {
// Register the server for D2 documents
documentSelector: [{ scheme: "file", language: d2Lang }],
synchronize: {
configurationSection: d2ConfigSection,
},
};

// Create the language client and start the client.
langClient = new LanguageClient(
"D2LanguageServer",
"D2-Language Server",
serverOptions,
clientOptions
);

langClient.start();
}

/**
* Checks to see if the language server is up and running
* so features can be turned on/off appropriately
*/
export function isLanguageServerRunning(): boolean {
if (langClient.initializeResult === undefined) {
return false;
}
return true;
}

/**
*
* This function will be asked by the Markdown system to render
* a d2 snippit in a markdown file
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function extendMarkdownItWithD2(md: any): unknown {
md.use(mdItContainer, pluginKeyword, {});
export function extendMarkdownItWithD2(md: ExtAny) {
md.use(markdownContainer, pluginKeyword, {
anyClass: true,
validate: (info: string) => {
return info.trim() === pluginKeyword;
},
});

const highlight = md.options.highlight;
const nextHighlighter = md.options.highlight;
md.options.highlight = (code: string, lang: string) => {
if (lang === d2Lang) {
const activeEditor = path.parse(
window.activeTextEditor?.document.fileName ?? ""
).dir;

return d2Tasks.compile(code, activeEditor, (msg) => {
const retText = d2Tasks.compile(code, activeEditor, (msg) => {
outputChannel.appendInfo(msg);
});

return `<pre><div>${retText}</div></pre>`;
}
return highlight(code, lang);
return nextHighlighter(code, lang);
};

return md;
}
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/refreshTimer.ts → client/src/refreshTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ws } from "./extension";
type TimerCallback = () => void;

export class RefreshTimer {
timerId?: NodeJS.Timer;
timerId?: NodeJS.Timeout;
callback: TimerCallback;
interval = 0;

Expand Down
6 changes: 4 additions & 2 deletions src/taskRunner.ts → client/src/taskRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
tasks,
TaskScope,
} from "vscode";
import { outputChannel } from "./extension";
import { isLanguageServerRunning, outputChannel } from "./extension";
import { d2Tasks } from "./tasks";

// eslint-disable-next-line no-unused-vars
Expand Down Expand Up @@ -41,7 +41,9 @@ export class TaskRunner {
"D2 Task",
"D2 Extension",
ce,
["$D2Matcher"]
// If the languange server is not running, fall back to the
// standard problem matcher
isLanguageServerRunning() ? undefined : ["$D2Matcher"]
);

task.presentationOptions = {
Expand Down
3 changes: 3 additions & 0 deletions src/tasks.ts → client/src/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class D2Tasks {
filePath = undefined;
}

// Note: if env: is not specified, then process.env
// is the default, if anything is specified for env:,
// then process.env is not included (good bye path).
const proc = spawnSync(d2Path, args, {
cwd: filePath,
input: text,
Expand Down
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"strict": true,
"module": "commonjs",
"target": "es2022",
"lib": ["es2022"],
"esModuleInterop": false,
"sourceMap": true,
"outDir": "dist",
"rootDir": "src",
"declaration": true,
},
"include": ["src"],
"exclude": ["node_modules"]

}
Loading
Loading