Skip to content

Commit

Permalink
feat: languages page
Browse files Browse the repository at this point in the history
  • Loading branch information
Princesseuh committed Mar 26, 2024
1 parent fcd5a57 commit 40318d4
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 5 deletions.
4 changes: 4 additions & 0 deletions astro.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import starlight from "@astrojs/starlight";
import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections";
import { defineConfig } from "astro/config";

// https://astro.build/config
Expand All @@ -10,6 +11,9 @@ export default defineConfig({
src: "./src/assets/logo.svg",
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.",
},
expressiveCode: {
plugins: [pluginCollapsibleSections()],
},
title: "Volar.js",
description: "The Embedded Language Tooling Framework",
social: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"dependencies": {
"@astrojs/starlight": "^0.21.2",
"@expressive-code/plugin-collapsible-sections": "^0.33.5",
"@types/hast": "^3.0.4",
"astro": "^4.5.9",
"prettier": "^3.2.5",
Expand Down
55 changes: 55 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions src/content/docs/core-concepts/embedded-languages.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
title: Embedded languages
---

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.
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.

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/).
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.

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.
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.

**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.
**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
2 changes: 2 additions & 0 deletions src/content/docs/guides/first-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,5 @@ If you're curious, you can actually start the client right now by running the `L
Since the server doesn't exist yet, the client will fail to start and immediately crash, but, hey, it's a start!

## The server

... to be continued.
123 changes: 122 additions & 1 deletion src/content/docs/reference/languages.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,125 @@
title: Languages
---

> 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)
> 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)
As can be expected from a framework for language servers, Volar allows you to define the languages you want to support in your project.

## Shape of a language definition

A language definition is a JavaScript object that contains a `createVirtualCode` and a `updateVirtualCode` function.

```ts title="src/my-language.ts"
export const language = {
createVirtualCode(fileId, languageId, snapshot) {
// Create a virtual code object
},
updateVirtualCode(_fileId, languageCode, snapshot) {
// Update the virtual code object
},
};
```

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.

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.

```ts title="src/my-language.ts"
import type { LanguagePlugin, VirtualCode } from '@volar/language-core';

export const language = {
createVirtualCode(fileId, languageId, snapshot) {
if (languageId !== 'my-language')
return;

return new MyLanguageVirtualCode(snapshot);
},
updateVirtualCode(_fileId, languageCode, snapshot) {
languageCode.update(snapshot);
return languageCode;
},
} satisfies LanguagePlugin<MyLanguageVirtualCode>;

export class MyLanguageVirtualCode implements VirtualCode {
id = 'root';
languageId = 'my-language';
mappings = []

constructor(
public snapshot: ts.IScriptSnapshot
) {
this.onSnapshotUpdated();
}

public update(newSnapshot: ts.IScriptSnapshot) {
this.snapshot = newSnapshot;
this.onSnapshotUpdated();
}

onSnapshotUpdated() {
// Update the virtual code object
}
}
```

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.

### Embedded languages

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.

```ts title="src/my-language.ts" ins={20, 34-52} collapse={1-14, 22-31}
import type { LanguagePlugin, VirtualCode } from '@volar/language-core';

export const language = {
createVirtualCode(fileId, languageId, snapshot) {
if (languageId !== 'my-language')
return;

return new MyLanguageVirtualCode(snapshot);
},
updateVirtualCode(_fileId, languageCode, snapshot) {
languageCode.update(snapshot);
return languageCode;
},
} satisfies LanguagePlugin<MyLanguageVirtualCode>;

export class MyLanguageVirtualCode implements VirtualCode {
id = 'root';
languageId = 'my-language';
mappings = []
embeddedCodes: VirtualCode[] = []

constructor(
public snapshot: ts.IScriptSnapshot
) {
this.onSnapshotUpdated();
}

public update(newSnapshot: ts.IScriptSnapshot) {
this.snapshot = newSnapshot;
this.onSnapshotUpdated();
}

onSnapshotUpdated() {
const snapshotContent = this.snapshot.getText(0, this.snapshot.getLength());

// Find embedded languages
const embeddedLanguages = findEmbeddedLanguages(snapshotContent);

// Create virtual code objects for embedded languages
this.embeddedCodes = embeddedLanguages.map(embeddedLanguage => {
return {
id: embeddedLanguage.id,
languageId: embeddedLanguage.languageId,
mappings: [],
snapshot: {
getText: (start, end) => embeddedLanguage.content.substring(start, end),
getLength: () => embeddedLanguage.content.length,
getChangeRange: () => undefined,
}
}
});
}
}
```

0 comments on commit 40318d4

Please sign in to comment.