diff --git a/hugo/content/docs/introduction/features.md b/hugo/content/docs/introduction/features.md index e6fa716e..0a00ac81 100644 --- a/hugo/content/docs/introduction/features.md +++ b/hugo/content/docs/introduction/features.md @@ -19,7 +19,7 @@ In this chapter, you'll get a closer look at the requirements developers usually - [Cross References and Linking](#cross-references-and-linking) - [Workspace Management](#workspace-management) - [Editing Support](#editing-support) -- [Try it out!](#try-it-out) +- [Try it out](#try-it-out) Langium provides out-of-the-box solutions for these problems, with the ability to fine-tune every part of it to fit your domain requirements. @@ -27,7 +27,7 @@ Langium provides out-of-the-box solutions for these problems, with the ability t ## Language Parsing -Programming languages and domain specific languages (DSLs) cannot be parsed using simple regular expressions (RegExp). Instead they require a more sophisticated parsing strategy. To define a custom language in Langium, you interact with a high level representation of your context-free grammar using the [Langium grammar language](/docs/grammar-language), in a similar fashion to EBNF. +Programming languages and domain specific languages (DSLs) cannot be parsed using simple regular expressions (RegExp). Instead they require a more sophisticated parsing strategy. To define a custom language in Langium, you interact with a high level representation of your context-free grammar using the [Langium grammar language](/docs/reference/grammar-language), in a similar fashion to EBNF. Based on the grammar, Langium is then able to construct a parser which transforms an input string into a semantic model representation. Just as the name suggests, this model captures the essential structure to describe your language. @@ -97,7 +97,7 @@ The Langium framework is deeply integrated with the [language server protocol](h The LSP includes commonly used language features, such as code completion, custom validations/diagnostics, finding references, formatting and many more. This allows for deep IDE integration without binding your language to a single IDE. Langium offers out-of-the-box support for most of these language features, with additional extension points for your domain specific requirements. -## Try it out! +## Try it out You can try out most of these features using our [showcase](/showcase/) and [playground](/playground/). The languages shown there are written using Langium and integrated in the monaco-editor. diff --git a/hugo/content/docs/learn/minilogo/building_an_extension/index.md b/hugo/content/docs/learn/minilogo/building_an_extension/_index.md similarity index 100% rename from hugo/content/docs/learn/minilogo/building_an_extension/index.md rename to hugo/content/docs/learn/minilogo/building_an_extension/_index.md diff --git a/hugo/content/docs/learn/workflow/_index.md b/hugo/content/docs/learn/workflow/_index.md index b5b35caa..ddec04f2 100644 --- a/hugo/content/docs/learn/workflow/_index.md +++ b/hugo/content/docs/learn/workflow/_index.md @@ -1,7 +1,7 @@ --- title: "Langium's workflow" weight: 0 -url: /docs/learn/workflow/ +url: /docs/learn/workflow aliases: - /docs/getting-started --- diff --git a/hugo/content/docs/recipes/builtin-library.md b/hugo/content/docs/recipes/builtin-library.md index 7e53a6c3..974047cd 100644 --- a/hugo/content/docs/recipes/builtin-library.md +++ b/hugo/content/docs/recipes/builtin-library.md @@ -8,7 +8,7 @@ For example, TypeScript provides users with typings for globally accessible vari They are part of the JavaScript runtime, and not defined by any user or a package they might import. Instead, these features are contributed through what we call builtin libraries. -Loading a builtin library in Langium is very simple. We first start off with defining the source code of the library using the *hello world* language from the [getting started guide](/docs/getting-started): +Loading a builtin library in Langium is very simple. We first start off with defining the source code of the library using the *hello world* language from the [getting started guide](/docs/learn/workflow): ```ts export const builtinHelloWorld = ` diff --git a/hugo/content/docs/recipes/code-bundling.md b/hugo/content/docs/recipes/code-bundling.md index 9dcf91a6..1f2d0183 100644 --- a/hugo/content/docs/recipes/code-bundling.md +++ b/hugo/content/docs/recipes/code-bundling.md @@ -5,7 +5,7 @@ aliases: - /guides/code-bundling --- -When you first create a Langium project using the [Yeoman generator](/docs/learn/workflow/install), it will only contain a plain TypeScript configuration, without any additional build processes. +When you first create a Langium project using the [Yeoman generator](/docs/learn/workflow/scaffold#your-first-example-language), it will only contain a plain TypeScript configuration, without any additional build processes. However, if you want to make your language available for consumption in a non-development context, you'll want to create a bundle. It is not absolutely necessary in a Node.js context, since you can always resolve local `node_modules` but it's still recommended [for vscode extensions](https://code.visualstudio.com/api/working-with-extensions/bundling-extension). It improves performance and decreases file size by minifying your code and only including what you actually need. diff --git a/hugo/content/docs/recipes/multiple-languages.md b/hugo/content/docs/recipes/multiple-languages.md index d76d7065..112a20e9 100644 --- a/hugo/content/docs/recipes/multiple-languages.md +++ b/hugo/content/docs/recipes/multiple-languages.md @@ -25,7 +25,7 @@ The entire change touches several files. Let's summarize what needs to be done: ## Our scenario -To keep this guide easy, I will use the `hello-world` project from the [learning section](/docs/learn/workflow). +To keep this guide easy, I will use the [`hello-world` project](/docs/learn/workflow) of the learning section. Let’s imagine that we have three languages: diff --git a/hugo/content/docs/recipes/scoping/_index.md b/hugo/content/docs/recipes/scoping/_index.md index 9d683f4f..b97b2708 100644 --- a/hugo/content/docs/recipes/scoping/_index.md +++ b/hugo/content/docs/recipes/scoping/_index.md @@ -25,8 +25,8 @@ In general, the way we resolve references is split into three phases of the docu In this guide, we'll look at different scoping kinds and styles and see how we can achieve them using Langium: -1. [Qualified Name Scoping](./qualified-name) -2. [Class Member Scoping](./class-member) +1. [Qualified Name Scoping](/docs/recipes/scoping/qualified-name) +2. [Class Member Scoping](/docs/recipes/scoping/class-member) Note that these are just example implementations for commonly used scoping methods. The scoping API of Langium is designed to be flexible and extensible for any kind of use case. diff --git a/hugo/content/docs/recipes/scoping/qualified-name.md b/hugo/content/docs/recipes/scoping/qualified-name.md index 8f9ffbb1..715bcabd 100644 --- a/hugo/content/docs/recipes/scoping/qualified-name.md +++ b/hugo/content/docs/recipes/scoping/qualified-name.md @@ -23,7 +23,7 @@ void main() { As can be seen, using qualified name scoping is quite helpful in this case. It allows us to reference the `getDocumentation` function through the scope computed & made available by the `Langium` namespace, even though it's not directly accessible within the scope of `main` by itself. -Note that such behavior can also be accomplished using [class member scoping](./class-member). +Note that such behavior can also be accomplished using [class member scoping](/docs/recipes/scoping/class-member). However, there is one core advantage to using globally available elements: Compared to member scoping, this type of scoping requires few resources. The lookup required for qualified name scoping can be done in near constant time with just a bit of additional computation on a **per-document** basis, whereas member scoping needs to do a lot of computation on a **per-reference** basis. diff --git a/hugo/content/docs/reference/grammar-language.md b/hugo/content/docs/reference/grammar-language.md index e0d732b6..9bd6e7dd 100644 --- a/hugo/content/docs/reference/grammar-language.md +++ b/hugo/content/docs/reference/grammar-language.md @@ -11,7 +11,9 @@ aliases: The grammar language describes the syntax and structure of your language. The [Langium grammar language](https://github.com/eclipse-langium/langium/blob/main/packages/langium/src/grammar/langium-grammar.langium) is implemented using Langium itself and therefore follows the same syntactic rules as any language created with Langium. The grammar language will define the structure of the *abstract syntax tree* (AST) which in Langium is a collection of *TypeScript types* describing the content of a parsed document and organized hierarchically. The individual nodes of the tree are then represented with JavaScript objects at runtime. In the following, we describe the Langium syntax and document structure. + ## Language Declaration + An *entry* Langium grammar file (i.e. a grammar which contains an [entry rule](#the-entry-rule)) always starts with a header which declares the name of the language. For example, a language named `MyLanguage` would be declared with: ```langium @@ -80,7 +82,7 @@ Person: ``` In this example, the parser will create an object of type `Person`. This object will have a property `name` which value and type must match the terminal rule `ID` (i.e. the property `name` is of type `string` and cannot start with a digit or special character). -By default, the parser will create an object with an inferred type corresponding to the parser rule name. It is possible to override this behavior by explicitly defining the type of the object to be created. This is done by adding the keyword `returns` followed by a separately declared type, or the keyword `infers` followed by the name of the type to be inferred for this rule (more about this [in the next chapter](../sematic-model)): +By default, the parser will create an object with an inferred type corresponding to the parser rule name. It is possible to override this behavior by explicitly defining the type of the object to be created. This is done by adding the keyword `returns` followed by a separately declared type, or the keyword `infers` followed by the name of the type to be inferred for this rule (more about this [in the next chapter](/docs/reference/semantic-model)): ```langium Person infers OtherType: 'person' name=ID; diff --git a/hugo/content/docs/reference/semantic-model.md b/hugo/content/docs/reference/semantic-model.md index ac5f9a02..151bb111 100644 --- a/hugo/content/docs/reference/semantic-model.md +++ b/hugo/content/docs/reference/semantic-model.md @@ -92,7 +92,7 @@ interface X extends AstNode { ``` ### Assignments -There are three available kinds of [assignments](../grammar-language/#assignments) in a parser rule: +There are three available kinds of [assignments](/docs/reference/grammar-language/#assignments) in a parser rule: 1. `=` for assigning a **single value** to a property, resulting in the property's type to be derived from the right hand side of the assignment. 2. `+=` for assigning **multiple values** to a property, resulting in the property's type to be an array of the right hand side of the assignment. diff --git a/package-lock.json b/package-lock.json index dc558775..aeaa20cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "tailwind" ], "devDependencies": { + "@types/chalk": "^2.2.0", + "chalk": "^4.0.0", "concurrently": "~8.2.1", "cross-env": "~7.0.3", "front-matter": "^4.0.2", @@ -1770,6 +1772,16 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/chalk": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-2.2.0.tgz", + "integrity": "sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw==", + "deprecated": "This is a stub types definition for chalk (https://github.com/chalk/chalk). chalk provides its own type definitions, so you don't need @types/chalk installed!", + "dev": true, + "dependencies": { + "chalk": "*" + } + }, "node_modules/@types/css-font-loading-module": { "version": "0.0.13", "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.13.tgz", diff --git a/package.json b/package.json index f2a0f256..51fdc8d5 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "check:links": "ts-node scripts/check-links.ts" }, "devDependencies": { + "@types/chalk": "^2.2.0", + "chalk": "^4.0.0", "concurrently": "~8.2.1", "cross-env": "~7.0.3", "front-matter": "^4.0.2", diff --git a/scripts/check-links.ts b/scripts/check-links.ts index f01378b9..756429df 100644 --- a/scripts/check-links.ts +++ b/scripts/check-links.ts @@ -1,6 +1,7 @@ import { glob } from "glob"; -import { mkdir, readFile } from "node:fs/promises"; +import { readFile } from "node:fs/promises"; import { basename, dirname, resolve, relative } from 'node:path'; +import chalk from 'chalk'; import fm from "front-matter"; type Attributes = { @@ -11,41 +12,56 @@ type Attributes = { type MarkdownFile = { localPath: string; + documentLink: string; aliases: string[]; links: string[]; } +type LinkType = 'alias'|'document'; + const projectDir = dirname(__dirname); const contentDir = resolve(projectDir, "hugo", "content"); async function main() { - let success = true; - const markdownFiles: MarkdownFile[] = await readMarkdownFiles(); - //collect what is there - const setOfUrls = new Set([ + const markdownFiles = await readMarkdownFiles(); + const setOfUrls = classifyAsDocumentLinksOrAliases(markdownFiles); + //await writeFile("existingLinks.txt", JSON.stringify([...setOfUrls.entries()], null, 2)); + return printMissingLinks(markdownFiles, setOfUrls); +} + +function classifyAsDocumentLinksOrAliases(markdownFiles: MarkdownFile[]) { + const documentLinks = [ + ...markdownFiles.map(m => m.documentLink), 'http://langium.org/', "/showcase", "/playground" - ].map(urlToString)); - for (const file of markdownFiles) { - for (const url of file.aliases) { - setOfUrls.add(urlToString(url)); - } - } - //check what is missing + ].map(urlToString).map(s => [s, 'document'] as const); + const aliases = markdownFiles.flatMap(m => m.aliases).map(urlToString).map(s => [s, 'alias'] as const); + return new Map([...documentLinks, ...aliases]); +} + +function printMissingLinks(markdownFiles: MarkdownFile[], setOfUrls: Map) { + let success: boolean = true; for (const file of markdownFiles) { let out = false; for (const link of file.links) { - if(link.startsWith("http") || link.endsWith(".png") || link.endsWith(".jpg")) { + if (link.startsWith("http") || link.endsWith(".png") || link.endsWith(".jpg")) { continue; } const url = urlToString(link); - if(!setOfUrls.has(url)) { - if(!out) { + if (!setOfUrls.has(url)) { + if (!out) { + console.log(`${relative(contentDir, file.localPath)}:`); + out = true; + } + console.log(`- ${chalk.red("MISSING LINK")}: ${url.toString()}`); + success = false; + } else if(setOfUrls.get(url) === 'alias') { + if (!out) { console.log(`${relative(contentDir, file.localPath)}:`); out = true; } - console.log(`- MISSING LINK: ${url.toString()}`); + console.log(`- ${chalk.yellow("LINK TO ALIAS")}: ${url.toString()}`); success = false; } } @@ -65,16 +81,17 @@ async function readMarkdownFiles() { if (aliases) { urls = [...urls, ...aliases]; } + let documentLink: string = ''; if (url) { - urls.push(url); + documentLink = url; } else if (slug) { - urls.push(relative(contentDir, resolve(mdFile, '..', slug))); + documentLink = relative(contentDir, resolve(mdFile, '..', slug)); } else { const base = basename(mdFile, '.md'); if (["index.md", "_index.md", '_index'].includes(base)) { - urls.push(relative(contentDir, resolve(mdFile, '..'))); + documentLink = relative(contentDir, resolve(mdFile, '..')); } else { - urls.push(relative(contentDir, resolve(mdFile, '..', base))); + documentLink = relative(contentDir, resolve(mdFile, '..', base)); } } @@ -84,6 +101,7 @@ async function readMarkdownFiles() { //new file markdownFiles.push({ localPath: mdFile, + documentLink, aliases: urls, links });