Skip to content

Commit 40318d4

Browse files
committed
feat: languages page
1 parent fcd5a57 commit 40318d4

File tree

6 files changed

+188
-5
lines changed

6 files changed

+188
-5
lines changed

astro.config.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import starlight from "@astrojs/starlight";
2+
import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections";
23
import { defineConfig } from "astro/config";
34

45
// https://astro.build/config
@@ -10,6 +11,9 @@ export default defineConfig({
1011
src: "./src/assets/logo.svg",
1112
alt: "Volar's logo, a light-blue prism with a very subtle tint of red in the top right and a wave going through the prism.",
1213
},
14+
expressiveCode: {
15+
plugins: [pluginCollapsibleSections()],
16+
},
1317
title: "Volar.js",
1418
description: "The Embedded Language Tooling Framework",
1519
social: {

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
},
1313
"dependencies": {
1414
"@astrojs/starlight": "^0.21.2",
15+
"@expressive-code/plugin-collapsible-sections": "^0.33.5",
1516
"@types/hast": "^3.0.4",
1617
"astro": "^4.5.9",
1718
"prettier": "^3.2.5",

pnpm-lock.yaml

+55
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/content/docs/core-concepts/embedded-languages.mdx

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
title: Embedded languages
33
---
44

5-
On the web, most programming languages are composed of other programming languages. The most common example is HTML, in which you will find JavaScript and CSS in `<script>` and `<style>` blocks respectively. You can even find some inline CSS in the `style` attribute of HTML elements, and some inline JavaScript in the `onclick` (and other events) attribute of HTML elements.
5+
A lot of programming languages used these days are composed of other programming languages. The most famous example is HTML, in which you will find JavaScript and CSS in `<script>` and `<style>` blocks respectively. In addition to those, you can even find some inline CSS in the `style` attribute of HTML elements, and some inline JavaScript in the `onclick` (and other events) attribute of HTML elements.
66

7-
There is actually a remarkable number of examples of this behaviour: The different blocks in [Vue's Single-File Components](https://vuejs.org/guide/scaling-up/sfc.html), the sections in [Svelte's components](https://svelte.dev/docs/svelte-components) or the frontmatter syntax and JSX-based component template of [Astro's components](https://docs.astro.build/en/core-concepts/astro-components/).
7+
There is actually a remarkable number of examples of this behaviour, especially on the web: The different blocks in [Vue's Single-File Components](https://vuejs.org/guide/scaling-up/sfc.html), the sections in [Svelte's components](https://svelte.dev/docs/svelte-components), the frontmatter syntax and JSX-based component template of [Astro's components](https://docs.astro.build/en/core-concepts/astro-components/), the different blocks in [Markdown](https://spec.commonmark.org/0.29/#fenced-code-blocks) (not to mention the common YAML frontmatter), and the list goes on.
88

9-
As such, Volar was built on the principle that it is highly likely that your language will have such embedded languages and has top-notch support for them. Notably, Volar can drastically simplify the mappings needed to get editing features working across the different parts of your files.
9+
As such, Volar was built on the principle that it is fairly likely that your language will have such embedded languages and has top-notch support for them. Notably, Volar can drastically simplify the mappings needed to get editing features working across the different parts of your files.
1010

11-
**If your language does not have embedded languages: That's completely okay!** Volar will still work for you just as well, and it will be ready if one day you do decide to add embedded languages to your language.
11+
**If your language does not have embedded languages: That's completely okay!** Volar will still work for you just as well, and it will be ready if one day you do decide to add embedded languages to your language

src/content/docs/guides/first-server.mdx

+2
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,5 @@ If you're curious, you can actually start the client right now by running the `L
299299
Since the server doesn't exist yet, the client will fail to start and immediately crash, but, hey, it's a start!
300300

301301
## The server
302+
303+
... to be continued.

src/content/docs/reference/languages.mdx

+122-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,125 @@
22
title: Languages
33
---
44

5-
> This page is a stub. Interested in contributing some documentation to it, or want to improve it? [Edit this page on GitHub](https://github.com/volarjs/docs/blob/main/src/content/docs/reference/languages.mdx)
5+
> This page is a work in progress. Interested in contributing some documentation to it, or want to improve it? [Edit this page on GitHub](https://github.com/volarjs/docs/blob/main/src/content/docs/reference/languages.mdx)
6+
7+
As can be expected from a framework for language servers, Volar allows you to define the languages you want to support in your project.
8+
9+
## Shape of a language definition
10+
11+
A language definition is a JavaScript object that contains a `createVirtualCode` and a `updateVirtualCode` function.
12+
13+
```ts title="src/my-language.ts"
14+
export const language = {
15+
createVirtualCode(fileId, languageId, snapshot) {
16+
// Create a virtual code object
17+
},
18+
updateVirtualCode(_fileId, languageCode, snapshot) {
19+
// Update the virtual code object
20+
},
21+
};
22+
```
23+
24+
As the names suggests, those methods create and update a `VirtualCode`. A `VirtualCode` object is created for each file that your language server will handle. These can then be accessed in the hooks of the [services](/reference/services) that provides the features of your language server, as such they're also a place you can store additional information about the file that could be useful to know for the features of your services.
25+
26+
Albeit not required, a common pattern is to define a JavaScript class that implements the `VirtualCode` interface, as this makes it easier to later add more properties and methods to the virtual code object and unlock the ability to use `instanceof` to check if a virtual code object is of a certain type.
27+
28+
```ts title="src/my-language.ts"
29+
import type { LanguagePlugin, VirtualCode } from '@volar/language-core';
30+
31+
export const language = {
32+
createVirtualCode(fileId, languageId, snapshot) {
33+
if (languageId !== 'my-language')
34+
return;
35+
36+
return new MyLanguageVirtualCode(snapshot);
37+
},
38+
updateVirtualCode(_fileId, languageCode, snapshot) {
39+
languageCode.update(snapshot);
40+
return languageCode;
41+
},
42+
} satisfies LanguagePlugin<MyLanguageVirtualCode>;
43+
44+
export class MyLanguageVirtualCode implements VirtualCode {
45+
id = 'root';
46+
languageId = 'my-language';
47+
mappings = []
48+
49+
constructor(
50+
public snapshot: ts.IScriptSnapshot
51+
) {
52+
this.onSnapshotUpdated();
53+
}
54+
55+
public update(newSnapshot: ts.IScriptSnapshot) {
56+
this.snapshot = newSnapshot;
57+
this.onSnapshotUpdated();
58+
}
59+
60+
onSnapshotUpdated() {
61+
// Update the virtual code object
62+
}
63+
}
64+
```
65+
66+
This is a simple example of a language definition, where `MyVirtualLanguageCode` only does the strict minimum possible. In a real language definition, you would most likely have a lot more properties and methods available on the `MyLanguageVirtualCode` class.
67+
68+
### Embedded languages
69+
70+
If your language supports [embedded languages](/core-concepts/embedded-languages/), your instance `VirtualCode` should include a `embeddedCodes` property that contains an array of `VirtualCode` instances for the embedded languages.
71+
72+
```ts title="src/my-language.ts" ins={20, 34-52} collapse={1-14, 22-31}
73+
import type { LanguagePlugin, VirtualCode } from '@volar/language-core';
74+
75+
export const language = {
76+
createVirtualCode(fileId, languageId, snapshot) {
77+
if (languageId !== 'my-language')
78+
return;
79+
80+
return new MyLanguageVirtualCode(snapshot);
81+
},
82+
updateVirtualCode(_fileId, languageCode, snapshot) {
83+
languageCode.update(snapshot);
84+
return languageCode;
85+
},
86+
} satisfies LanguagePlugin<MyLanguageVirtualCode>;
87+
88+
export class MyLanguageVirtualCode implements VirtualCode {
89+
id = 'root';
90+
languageId = 'my-language';
91+
mappings = []
92+
embeddedCodes: VirtualCode[] = []
93+
94+
constructor(
95+
public snapshot: ts.IScriptSnapshot
96+
) {
97+
this.onSnapshotUpdated();
98+
}
99+
100+
public update(newSnapshot: ts.IScriptSnapshot) {
101+
this.snapshot = newSnapshot;
102+
this.onSnapshotUpdated();
103+
}
104+
105+
onSnapshotUpdated() {
106+
const snapshotContent = this.snapshot.getText(0, this.snapshot.getLength());
107+
108+
// Find embedded languages
109+
const embeddedLanguages = findEmbeddedLanguages(snapshotContent);
110+
111+
// Create virtual code objects for embedded languages
112+
this.embeddedCodes = embeddedLanguages.map(embeddedLanguage => {
113+
return {
114+
id: embeddedLanguage.id,
115+
languageId: embeddedLanguage.languageId,
116+
mappings: [],
117+
snapshot: {
118+
getText: (start, end) => embeddedLanguage.content.substring(start, end),
119+
getLength: () => embeddedLanguage.content.length,
120+
getChangeRange: () => undefined,
121+
}
122+
}
123+
});
124+
}
125+
}
126+
```

0 commit comments

Comments
 (0)