@cosense-toolbox/lsp 0.1.0-beta.6 → 0.1.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"link.d.mts","names":[],"sources":["../src/link.ts"],"mappings":";;;;;;;;;UAYiB;WACN;WACA;;WAEA;WACA;WACA,OAAO;;;UAID;WACN;WACA;WACA,OAAO;;;KAIN,aAAa,aAAa;;;;;;;;;cA4GzB,SACX,cACA,UAAU,UACV,eAAc,iBACb,OAAO,OAAO"}
1
+ {"version":3,"file":"link.d.mts","names":[],"sources":["../src/link.ts"],"mappings":";;;;;;;;;UAYiB;WACN;WACA;;WAEA;WACA;WACA,OAAO;;;UAID;WACN;WACA;WACA,OAAO;;;KAIN,aAAa,aAAa;;;;;;;;;cAgHzB,SACX,cACA,UAAU,UACV,eAAc,iBACb,OAAO,OAAO"}
package/dist/link.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { n as gather, r as leaf, t as branch } from "./tree-sXzD2703.mjs";
2
2
  import { Array, Match, Option, pipe } from "effect";
3
- import { normalizeLineEndings, parse } from "@cosense-toolbox/parser";
3
+ import { asMapUrl, normalizeLineEndings, parse } from "@cosense-toolbox/parser";
4
4
  //#region src/link.ts
5
5
  /** The id Cosense gives every line, after the `#` that ends a link to it. */
6
6
  const LINE_LINK = /^(.+)#([0-9a-f]{24})$/;
@@ -61,6 +61,10 @@ const targetsOf = (lines) => (node) => Match.value(node).pipe(Match.discriminato
61
61
  icon: ({ user, position }) => leaf([iconPageOf(rangeOf(position), user)]),
62
62
  externalLink: ({ target, position }) => leaf([urlOf(rangeOf(position), target)]),
63
63
  image: ({ src, link, position }) => leaf([urlOf(rangeOf(position), link ?? src)]),
64
+ video: ({ src, link, position }) => leaf([urlOf(rangeOf(position), link ?? src)]),
65
+ audio: ({ src, position }) => leaf([urlOf(rangeOf(position), src)]),
66
+ embed: ({ url, position }) => leaf([urlOf(rangeOf(position), url)]),
67
+ location: (node) => leaf([urlOf(rangeOf(node.position), asMapUrl(node))]),
64
68
  decoration: ({ value, position }) => pipe(largeIconOf(lines, rangeOf(position), value), Option.match({
65
69
  onNone: () => branch([]),
66
70
  onSome: (page) => leaf([page])
package/dist/link.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"link.mjs","names":["Arr"],"sources":["../src/link.ts"],"sourcesContent":["import type { AnyNode, ParseOptions, Position as NodePosition } from \"@cosense-toolbox/parser\"\nimport { normalizeLineEndings, parse } from \"@cosense-toolbox/parser\"\nimport { Array as Arr, Match, Option, pipe } from \"effect\"\nimport type { Position, Range } from \"vscode-languageserver/node\"\n\nimport { branch, gather, leaf, type Picked } from \"./tree\"\n\n/**\n * A page a link leads to. `project` is there only when the link names one\n * (`[/help-jp/page]`); without it the page is in the reader's own project. `lineId` is\n * there only when the link names a line of the page (`[page#<line id>]`).\n */\nexport interface PageTarget {\n readonly kind: \"page\"\n readonly project?: string\n /** As written: empty for `[/project]`, which leads to the project rather than a page. */\n readonly title: string\n readonly lineId?: string\n readonly range: Range\n}\n\n/** A URL a link leads to: an external link, or an image and where it links. */\nexport interface UrlTarget {\n readonly kind: \"url\"\n readonly url: string\n readonly range: Range\n}\n\n/** What a link leads to, and the range of the whole notation it is written as. */\nexport type LinkTarget = PageTarget | UrlTarget\n\n/** The id Cosense gives every line, after the `#` that ends a link to it. */\nconst LINE_LINK = /^(.+)#([0-9a-f]{24})$/\n\nconst ICON = /^(.+)\\.icon(?:\\*\\d+)?$/\n\n/** `/project/name`, as an icon of another project writes its user. */\nconst OF_PROJECT = /^\\/([^/]+)\\/(.+)$/\n\n/** `{ [key]: value }` when there is a value, else nothing: an absent field, not `undefined`. */\nconst fieldOf = <K extends string, A>(key: K, value: Option.Option<A>) =>\n Option.match(value, {\n onNone: () => ({}),\n onSome: (present) => ({ [key]: present }) as { readonly [P in K]: A },\n })\n\n/** The parser counts columns in UTF-16 units, as the LSP does, so they carry over as is. */\nconst rangeOf = ({ start, end }: NodePosition): Range => ({\n start: { line: start.line, character: start.column },\n end: { line: end.line, character: end.column },\n})\n\nconst pageOf = (range: Range, project: Option.Option<string>, written: string): PageTarget =>\n pipe(\n Option.fromNullable(LINE_LINK.exec(written)),\n Option.match({\n onNone: () => ({ title: written, lineId: Option.none<string>() }),\n onSome: ([, title = \"\", lineId = \"\"]) => ({ title, lineId: Option.some(lineId) }),\n }),\n ({ title, lineId }) => ({\n kind: \"page\",\n ...fieldOf(\"project\", project),\n title,\n ...fieldOf(\"lineId\", lineId),\n range,\n }),\n )\n\nconst urlOf = (range: Range, url: string): UrlTarget => ({ kind: \"url\", url, range })\n\n/** The page of an icon's user, who may be of another project (`/icons/taro`). */\nconst iconPageOf = (range: Range, user: string): PageTarget =>\n pipe(\n Option.fromNullable(OF_PROJECT.exec(user)),\n Option.match({\n onNone: () => pageOf(range, Option.none(), user),\n onSome: ([, project = \"\", name = \"\"]) => pageOf(range, Option.some(project), name),\n }),\n )\n\n/**\n * `[[taro.icon]]`, which the parser reads as bold text (`[[x]]`) rather than as an icon.\n * Only the double bracket counts: `[* taro.icon]` is bold text that Cosense shows as is.\n */\nconst largeIconOf = (\n lines: ReadonlyArray<string>,\n range: Range,\n value: string,\n): Option.Option<PageTarget> =>\n pipe(\n Option.fromNullable(lines[range.start.line]),\n Option.filter((line) => line.startsWith(\"[[\", range.start.character)),\n Option.flatMap(() => Option.fromNullable(ICON.exec(value))),\n Option.map(([, user = \"\"]) => iconPageOf(range, user)),\n )\n\nconst targetsOf =\n (lines: ReadonlyArray<string>) =>\n (node: AnyNode): Picked<LinkTarget> =>\n Match.value(node).pipe(\n Match.discriminators(\"type\")({\n internalLink: ({ target, position }) =>\n leaf([pageOf(rangeOf(position), Option.none(), target)]),\n hashtag: ({ value, position }) => leaf([pageOf(rangeOf(position), Option.none(), value)]),\n projectLink: ({ project, title, position }) =>\n leaf([pageOf(rangeOf(position), Option.some(project), title)]),\n icon: ({ user, position }) => leaf([iconPageOf(rangeOf(position), user)]),\n externalLink: ({ target, position }) => leaf([urlOf(rangeOf(position), target)]),\n // Where the image leads, when it leads anywhere, is what following it opens.\n image: ({ src, link, position }) => leaf([urlOf(rangeOf(position), link ?? src)]),\n // A decoration can wrap links (`[! [page]です]`), so it is read into.\n decoration: ({ value, position }) =>\n pipe(\n largeIconOf(lines, rangeOf(position), value),\n Option.match({ onNone: () => branch([]), onSome: (page) => leaf([page]) }),\n ),\n }),\n Match.orElse(() => branch([])),\n )\n\n/**\n * Whether `character` is on the notation at `range`: on any of its characters, or just\n * after the last one, where the cursor is left after typing it.\n */\nconst touches = (range: Range, { line, character }: Position): boolean =>\n range.start.line === line &&\n range.start.character <= character &&\n character <= range.end.character\n\n/**\n * The link at `position`, and what it leads to. None where there is no link: on plain text,\n * on the markers of a decoration, in code.\n *\n * Resolving the target (to a file, a URL, a page of the web) is the caller's: it may be\n * asynchronous, or fail, in ways only the caller knows. `parseOptions` are the site's\n * notation extensions, so a bracket reads as the site's build reads it.\n */\nexport const linkAt = (\n text: string,\n position: Position,\n parseOptions: ParseOptions = {},\n): Option.Option<LinkTarget> => {\n const normalized = normalizeLineEndings(text)\n const lines = normalized.split(\"\\n\")\n const under = Arr.filter(gather(parse(normalized, parseOptions), targetsOf(lines)), (target) =>\n touches(target.range, position),\n )\n // Two notations can touch (`[a][b]`): the one that starts under the cursor is the one\n // it is on, and the one it is just after is only a fallback.\n return pipe(\n Arr.findFirst(under, ({ range }) => position.character < range.end.character),\n Option.orElse(() => Arr.head(under)),\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"link.mjs","names":["Arr"],"sources":["../src/link.ts"],"sourcesContent":["import type { AnyNode, ParseOptions, Position as NodePosition } from \"@cosense-toolbox/parser\"\nimport { asMapUrl, normalizeLineEndings, parse } from \"@cosense-toolbox/parser\"\nimport { Array as Arr, Match, Option, pipe } from \"effect\"\nimport type { Position, Range } from \"vscode-languageserver/node\"\n\nimport { branch, gather, leaf, type Picked } from \"./tree\"\n\n/**\n * A page a link leads to. `project` is there only when the link names one\n * (`[/help-jp/page]`); without it the page is in the reader's own project. `lineId` is\n * there only when the link names a line of the page (`[page#<line id>]`).\n */\nexport interface PageTarget {\n readonly kind: \"page\"\n readonly project?: string\n /** As written: empty for `[/project]`, which leads to the project rather than a page. */\n readonly title: string\n readonly lineId?: string\n readonly range: Range\n}\n\n/** A URL a link leads to: an external link, or an image and where it links. */\nexport interface UrlTarget {\n readonly kind: \"url\"\n readonly url: string\n readonly range: Range\n}\n\n/** What a link leads to, and the range of the whole notation it is written as. */\nexport type LinkTarget = PageTarget | UrlTarget\n\n/** The id Cosense gives every line, after the `#` that ends a link to it. */\nconst LINE_LINK = /^(.+)#([0-9a-f]{24})$/\n\nconst ICON = /^(.+)\\.icon(?:\\*\\d+)?$/\n\n/** `/project/name`, as an icon of another project writes its user. */\nconst OF_PROJECT = /^\\/([^/]+)\\/(.+)$/\n\n/** `{ [key]: value }` when there is a value, else nothing: an absent field, not `undefined`. */\nconst fieldOf = <K extends string, A>(key: K, value: Option.Option<A>) =>\n Option.match(value, {\n onNone: () => ({}),\n onSome: (present) => ({ [key]: present }) as { readonly [P in K]: A },\n })\n\n/** The parser counts columns in UTF-16 units, as the LSP does, so they carry over as is. */\nconst rangeOf = ({ start, end }: NodePosition): Range => ({\n start: { line: start.line, character: start.column },\n end: { line: end.line, character: end.column },\n})\n\nconst pageOf = (range: Range, project: Option.Option<string>, written: string): PageTarget =>\n pipe(\n Option.fromNullable(LINE_LINK.exec(written)),\n Option.match({\n onNone: () => ({ title: written, lineId: Option.none<string>() }),\n onSome: ([, title = \"\", lineId = \"\"]) => ({ title, lineId: Option.some(lineId) }),\n }),\n ({ title, lineId }) => ({\n kind: \"page\",\n ...fieldOf(\"project\", project),\n title,\n ...fieldOf(\"lineId\", lineId),\n range,\n }),\n )\n\nconst urlOf = (range: Range, url: string): UrlTarget => ({ kind: \"url\", url, range })\n\n/** The page of an icon's user, who may be of another project (`/icons/taro`). */\nconst iconPageOf = (range: Range, user: string): PageTarget =>\n pipe(\n Option.fromNullable(OF_PROJECT.exec(user)),\n Option.match({\n onNone: () => pageOf(range, Option.none(), user),\n onSome: ([, project = \"\", name = \"\"]) => pageOf(range, Option.some(project), name),\n }),\n )\n\n/**\n * `[[taro.icon]]`, which the parser reads as bold text (`[[x]]`) rather than as an icon.\n * Only the double bracket counts: `[* taro.icon]` is bold text that Cosense shows as is.\n */\nconst largeIconOf = (\n lines: ReadonlyArray<string>,\n range: Range,\n value: string,\n): Option.Option<PageTarget> =>\n pipe(\n Option.fromNullable(lines[range.start.line]),\n Option.filter((line) => line.startsWith(\"[[\", range.start.character)),\n Option.flatMap(() => Option.fromNullable(ICON.exec(value))),\n Option.map(([, user = \"\"]) => iconPageOf(range, user)),\n )\n\nconst targetsOf =\n (lines: ReadonlyArray<string>) =>\n (node: AnyNode): Picked<LinkTarget> =>\n Match.value(node).pipe(\n Match.discriminators(\"type\")({\n internalLink: ({ target, position }) =>\n leaf([pageOf(rangeOf(position), Option.none(), target)]),\n hashtag: ({ value, position }) => leaf([pageOf(rangeOf(position), Option.none(), value)]),\n projectLink: ({ project, title, position }) =>\n leaf([pageOf(rangeOf(position), Option.some(project), title)]),\n icon: ({ user, position }) => leaf([iconPageOf(rangeOf(position), user)]),\n externalLink: ({ target, position }) => leaf([urlOf(rangeOf(position), target)]),\n // Where an image or a video leads, when it leads anywhere, is what following it opens.\n image: ({ src, link, position }) => leaf([urlOf(rangeOf(position), link ?? src)]),\n video: ({ src, link, position }) => leaf([urlOf(rangeOf(position), link ?? src)]),\n audio: ({ src, position }) => leaf([urlOf(rangeOf(position), src)]),\n embed: ({ url, position }) => leaf([urlOf(rangeOf(position), url)]),\n location: (node) => leaf([urlOf(rangeOf(node.position), asMapUrl(node))]),\n // A decoration can wrap links (`[! [page]です]`), so it is read into.\n decoration: ({ value, position }) =>\n pipe(\n largeIconOf(lines, rangeOf(position), value),\n Option.match({ onNone: () => branch([]), onSome: (page) => leaf([page]) }),\n ),\n }),\n Match.orElse(() => branch([])),\n )\n\n/**\n * Whether `character` is on the notation at `range`: on any of its characters, or just\n * after the last one, where the cursor is left after typing it.\n */\nconst touches = (range: Range, { line, character }: Position): boolean =>\n range.start.line === line &&\n range.start.character <= character &&\n character <= range.end.character\n\n/**\n * The link at `position`, and what it leads to. None where there is no link: on plain text,\n * on the markers of a decoration, in code.\n *\n * Resolving the target (to a file, a URL, a page of the web) is the caller's: it may be\n * asynchronous, or fail, in ways only the caller knows. `parseOptions` are the site's\n * notation extensions, so a bracket reads as the site's build reads it.\n */\nexport const linkAt = (\n text: string,\n position: Position,\n parseOptions: ParseOptions = {},\n): Option.Option<LinkTarget> => {\n const normalized = normalizeLineEndings(text)\n const lines = normalized.split(\"\\n\")\n const under = Arr.filter(gather(parse(normalized, parseOptions), targetsOf(lines)), (target) =>\n touches(target.range, position),\n )\n // Two notations can touch (`[a][b]`): the one that starts under the cursor is the one\n // it is on, and the one it is just after is only a fallback.\n return pipe(\n Arr.findFirst(under, ({ range }) => position.character < range.end.character),\n Option.orElse(() => Arr.head(under)),\n )\n}\n"],"mappings":"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"}
package/dist/tokens.d.mts CHANGED
@@ -9,7 +9,7 @@ import { ParseOptions } from "@cosense-toolbox/parser";
9
9
  * some send the LSP's own list and nothing more — so `quote` would simply never be drawn.
10
10
  * LEGEND below is what is sent, and LSP_TYPE is the translation.
11
11
  */
12
- declare const TOKEN_TYPES: readonly ["title", "link", "projectLink", "externalLink", "hashtag", "code", "codeBlock", "formula", "icon", "quote", "bold", "italic", "strike", "underline", "image", "table", "bold2", "bold3", "component", "attribute", "attributeValue", "expression", "frontmatter", "notation"];
12
+ declare const TOKEN_TYPES: readonly ["title", "link", "projectLink", "externalLink", "hashtag", "code", "codeBlock", "formula", "icon", "quote", "bold", "italic", "strike", "underline", "image", "media", "table", "bold2", "bold3", "component", "attribute", "attributeValue", "expression", "frontmatter", "notation"];
13
13
  type TokenType = (typeof TOKEN_TYPES)[number];
14
14
  /** Every token type but `notation`, whose tokens also carry a name. */
15
15
  type BuiltinTokenType = Exclude<TokenType, "notation">;
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.mts","names":[],"sources":["../src/tokens.ts"],"mappings":";;;;;;;;;;;cAqBa;KAgCD,oBAAoB;;KAG3B,mBAAmB,QAAQ;UAEtB;WACC;WACA;WACA;;KAGC,YACP;WAAkB,MAAM;MACxB;WAAkB;WAA2B;;;;;;;;;;UAUjC;WACN;WACA;;;;;;;;cASE;;;;;;;;cAwDA,WAAY,WAAW,cAAc,cAAY;cAkEjD,gBAAc;;cA2Gd,iBAAkB,OAAO,0BAAwB,OAAO;;;;;cAUxD,UACX,OAAO,uBACP,yBACC,OAAO;UA6IO;;WAEN;;WAEA,eAAe;;WAEf,YAAY,cAAc;;;;;WAK1B;;;cAIE,gBAAiB,cAAc,UAAS,yBAA4B;;;;;;cAqDpE,eACX,QAAQ,cAAc,WACtB,SAAQ"}
1
+ {"version":3,"file":"tokens.d.mts","names":[],"sources":["../src/tokens.ts"],"mappings":";;;;;;;;;;;cAqBa;KAkCD,oBAAoB;;KAG3B,mBAAmB,QAAQ;UAEtB;WACC;WACA;WACA;;KAGC,YACP;WAAkB,MAAM;MACxB;WAAkB;WAA2B;;;;;;;;;;UAUjC;WACN;WACA;;;;;;;;cASE;;;;;;;;cAyDA,WAAY,WAAW,cAAc,cAAY;cAsEjD,gBAAc;;cA2Gd,iBAAkB,OAAO,0BAAwB,OAAO;;;;;cAUxD,UACX,OAAO,uBACP,yBACC,OAAO;UA6IO;;WAEN;;WAEA,eAAe;;WAEf,YAAY,cAAc;;;;;WAK1B;;;cAIE,gBAAiB,cAAc,UAAS,yBAA4B;;;;;;cAqDpE,eACX,QAAQ,cAAc,WACtB,SAAQ"}
package/dist/tokens.mjs CHANGED
@@ -26,6 +26,7 @@ const TOKEN_TYPES = [
26
26
  "strike",
27
27
  "underline",
28
28
  "image",
29
+ "media",
29
30
  "table",
30
31
  "bold2",
31
32
  "bold3",
@@ -69,6 +70,7 @@ const LSP_TYPE = {
69
70
  externalLink: "string",
70
71
  icon: "function",
71
72
  image: "string",
73
+ media: "string",
72
74
  hashtag: "decorator",
73
75
  code: "string",
74
76
  codeBlock: "string",
@@ -104,6 +106,10 @@ const INLINE_TOKEN_TYPE = {
104
106
  hashtag: "hashtag",
105
107
  inlineCode: "code",
106
108
  image: "image",
109
+ video: "media",
110
+ audio: "media",
111
+ embed: "media",
112
+ location: "media",
107
113
  icon: "icon",
108
114
  formula: "formula"
109
115
  };
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.mjs","names":["Arr"],"sources":["../src/tokens.ts"],"sourcesContent":["import type {\n AnyNode,\n AnyNodeType,\n Decoration,\n NodeOfType,\n ParseOptions,\n Position,\n} from \"@cosense-toolbox/parser\"\nimport { normalizeLineEndings, parse } from \"@cosense-toolbox/parser\"\nimport { Array as Arr, Match, Option, Order, pipe } from \"effect\"\n\nimport { branch, gather, leaf, type Picked } from \"./tree\"\n\n/**\n * Cosense notation as LSP semantic tokens.\n *\n * These names are the ones this package reasons in, and they are not what goes on the wire.\n * A client announces the token types it knows at initialize and drops everything else —\n * some send the LSP's own list and nothing more — so `quote` would simply never be drawn.\n * LEGEND below is what is sent, and LSP_TYPE is the translation.\n */\nexport const TOKEN_TYPES = [\n \"title\",\n \"link\",\n \"projectLink\",\n \"externalLink\",\n \"hashtag\",\n \"code\",\n \"codeBlock\",\n \"formula\",\n \"icon\",\n \"quote\",\n \"bold\",\n \"italic\",\n \"strike\",\n \"underline\",\n \"image\",\n \"table\",\n \"bold2\",\n \"bold3\",\n // `.csnx` only: a line that opens with a component tag. The tag's name, its attribute\n // names, and the two kinds of value, as JSX reads them.\n \"component\",\n \"attribute\",\n \"attributeValue\",\n \"expression\",\n // The YAML at the top of the file, which is not Cosense notation at all.\n \"frontmatter\",\n // A notation the caller defines (see `Notation`), such as `[! 注意]`. The token carries\n // the name the caller gave it.\n \"notation\",\n] as const\n\nexport type TokenType = (typeof TOKEN_TYPES)[number]\n\n/** Every token type but `notation`, whose tokens also carry a name. */\ntype BuiltinTokenType = Exclude<TokenType, \"notation\">\n\ninterface Span {\n readonly line: number\n readonly char: number\n readonly length: number\n}\n\nexport type RawToken =\n | (Span & { readonly type: BuiltinTokenType })\n | (Span & { readonly type: \"notation\"; readonly name: string })\n\n/**\n * A name the caller gives to a decoration marker, as `warning` to the `!` of `[! 注意]`: the\n * token type that decoration is sent as.\n *\n * The marker has to be one Cosense decorates with (`` !\"#%&'()*+,-./{|}<>_~= ``), since\n * those are the ones the parser reads as a decoration. Any other, such as `@`, leaves the\n * bracket a link, as it is in Cosense Web: how a notation looks does not change how it parses.\n */\nexport interface Notation {\n readonly marker: string\n readonly name: string\n}\n\n/**\n * The legend sent to the client: the LSP's own token types, in its own order.\n *\n * Every entry is in the set a client is expected to understand, which is what makes the\n * colours appear without the reader configuring anything.\n */\nexport const LEGEND = [\n \"namespace\",\n \"type\",\n \"function\",\n \"variable\",\n \"property\",\n \"parameter\",\n \"decorator\",\n \"macro\",\n \"keyword\",\n \"string\",\n \"number\",\n \"comment\",\n] as const\n\n/**\n * What each notation is drawn as. Chosen so that things a reader separates by eye land on\n * different entries: a link is not a tag, and a quote is not a heading. `bold` and its\n * louder siblings share one entry, because the LSP has no notion of weight and a theme\n * gives each type a single colour.\n */\nconst LSP_TYPE: Record<TokenType, (typeof LEGEND)[number]> = {\n title: \"namespace\",\n link: \"function\",\n projectLink: \"function\",\n externalLink: \"string\",\n icon: \"function\",\n image: \"string\",\n hashtag: \"decorator\",\n code: \"string\",\n codeBlock: \"string\",\n formula: \"number\",\n table: \"property\",\n quote: \"comment\",\n frontmatter: \"comment\",\n component: \"type\",\n attribute: \"parameter\",\n attributeValue: \"string\",\n expression: \"variable\",\n bold: \"keyword\",\n bold2: \"keyword\",\n bold3: \"keyword\",\n italic: \"macro\",\n strike: \"comment\",\n underline: \"variable\",\n // What a caller's notation is sent as when the legend has no type of its name.\n notation: \"decorator\",\n}\n\n/**\n * LEGEND with the names of `notations` after it, each once: the legend for a client that\n * accepts token types of the caller's own naming. The LSP's types keep their places.\n *\n * A client that colours by this package's own names instead passes those to `encodeTokens`\n * (`[...TOKEN_TYPES, ...names]`): a token whose own name is in the legend is sent as that.\n */\nexport const legendOf = (notations: ReadonlyArray<Notation>): ReadonlyArray<string> =>\n Arr.dedupe([...LEGEND, ...Arr.map(notations, (notation) => notation.name)])\n\n/** Leaf inline nodes that map 1:1. Each sits on one line, so no splitting is needed. */\nconst INLINE_TOKEN_TYPE: Partial<Record<AnyNodeType, BuiltinTokenType>> = {\n internalLink: \"link\",\n externalLink: \"externalLink\",\n projectLink: \"projectLink\",\n hashtag: \"hashtag\",\n inlineCode: \"code\",\n image: \"image\",\n icon: \"icon\",\n formula: \"formula\",\n}\n\n/** Top to bottom, then left to right: the order the LSP encoding needs. */\nconst BY_POSITION: Order.Order<RawToken> = Order.combine(\n Order.mapInput(Order.number, (token: RawToken) => token.line),\n Order.mapInput(Order.number, (token: RawToken) => token.char),\n)\n\nconst onlyIf = <A>(condition: boolean, value: A): Option.Option<A> =>\n condition ? Option.some(value) : Option.none()\n\n/** A token over `[from, to)` of one line. */\nconst token = (type: BuiltinTokenType, line: number, from: number, to: number): RawToken => ({\n line,\n char: from,\n length: to - from,\n type,\n})\n\nconst spanToken = (type: BuiltinTokenType, position: Position): RawToken =>\n token(type, position.start.line, position.start.column, position.end.column)\n\n/** How many characters from `from` the `^`-anchored `pattern` takes; 0 when it takes none. */\nconst matchLength = (pattern: RegExp, text: string, from: number): number =>\n pipe(\n Option.fromNullable(pattern.exec(text.slice(from))),\n Option.match({ onNone: () => 0, onSome: ([matched]) => matched.length }),\n )\n\n// --- Emphasis ---------------------------------------------------------------------------\n\n/** Cosense sizes emphasis by asterisk count (`[*]`..`[*****]`); sizeLevel is 0-indexed. */\nconst boldTokenType = (sizeLevel: number): BuiltinTokenType =>\n Match.value(sizeLevel).pipe(\n Match.when(\n (level) => level >= 2,\n (): BuiltinTokenType => \"bold3\",\n ),\n Match.when(1, (): BuiltinTokenType => \"bold2\"),\n Match.orElse((): BuiltinTokenType => \"bold\"),\n )\n\n/** Every decoration a run carries, since Cosense applies all of them: `[-* x]` is both. */\nconst decorationTokenTypes = (node: Decoration): ReadonlyArray<BuiltinTokenType> =>\n Arr.getSomes([\n onlyIf(node.underline, \"underline\" as const),\n onlyIf(node.strike, \"strike\" as const),\n onlyIf(node.italic, \"italic\" as const),\n onlyIf(node.bold, boldTokenType(node.sizeLevel)),\n ])\n\n// --- Component lines (`.csnx`) ----------------------------------------------------------\n\nexport const COMPONENT_LINE = /^\\s*<\\/?[A-Z][A-Za-z0-9_.]*(\\s|\\/?>)/\nconst TAG_OPEN = /^(\\s*<\\/?)([A-Z][A-Za-z0-9_.]*)/\nconst ATTRIBUTE_NAME = /^[A-Za-z_:][A-Za-z0-9_:.-]*/\nconst SPACE = /^\\s*/\n\n/** Where a quoted value opened at `from` ends: after its closing quote, or at the line's end. */\nconst quotedEnd = (text: string, from: number): number =>\n pipe(\n text.indexOf(text.charAt(from), from + 1),\n Option.liftPredicate((close) => close >= 0),\n Option.match({ onNone: () => text.length, onSome: (close) => close + 1 }),\n )\n\n/** Where `{ ... }` opened at `from` ends, counting nested braces: after its `}`, or the line's end. */\nconst bracedEnd = (text: string, from: number): number =>\n pipe(\n text.slice(from).split(\"\"),\n Arr.scan(0, (depth, char) => (char === \"{\" ? depth + 1 : char === \"}\" ? depth - 1 : depth)),\n // scan starts with the depth before any character; the rest line up with the characters.\n Arr.drop(1),\n Arr.findFirstIndex((depth) => depth === 0),\n Option.match({ onNone: () => text.length, onSome: (index) => from + index + 1 }),\n )\n\ntype ValueType = \"attributeValue\" | \"expression\"\n\n/** The attribute value that starts at `from`: its type and where it ends. */\nconst valueAt = (text: string, from: number): Option.Option<readonly [ValueType, number]> =>\n Match.value(text.charAt(from)).pipe(\n Match.when(\n (char) => char === '\"' || char === \"'\",\n () => Option.some([\"attributeValue\", quotedEnd(text, from)] as const),\n ),\n Match.when(\"{\", () => Option.some([\"expression\", bracedEnd(text, from)] as const)),\n Match.orElse(() => Option.none()),\n )\n\n/** The tokens of one attribute, and where the next may start. */\ntype Step = readonly [ReadonlyArray<RawToken>, number]\n\n/** `at` past any whitespace. */\nconst skipSpace = (text: string, at: number): number => at + matchLength(SPACE, text, at)\n\n/** Whether the tag closes at `at`, with `>` or `/>`, or the line ends there. */\nconst closesAt = (text: string, at: number): boolean =>\n at >= text.length || text.startsWith(\">\", at) || text.startsWith(\"/>\", at)\n\n/**\n * An attribute named over `[at, nameEnd)`: the name, then `=` and a value if one follows.\n * A name with no `=` after it stands alone, as JSX reads `<Dialog open>`.\n */\nconst attribute = (line: number, text: string, at: number, nameEnd: number): Step => {\n const name = token(\"attribute\", line, at, nameEnd)\n const afterName = skipSpace(text, nameEnd)\n return pipe(\n Option.liftPredicate(afterName, (equals) => text.charAt(equals) === \"=\"),\n Option.map((equals) => skipSpace(text, equals + 1)),\n Option.match({\n onNone: (): Step => [[name], afterName],\n onSome: (valueStart): Step =>\n pipe(\n valueAt(text, valueStart),\n Option.match({\n onNone: (): Step => [[name], valueStart],\n onSome: ([type, end]): Step => [[name, token(type, line, valueStart, end)], end],\n }),\n ),\n }),\n )\n}\n\n/** One attribute from `from`, or None once the tag has closed. */\nconst attributeStep =\n (line: number, text: string) =>\n (from: number): Option.Option<Step> =>\n pipe(\n Option.liftPredicate(skipSpace(text, from), (at) => !closesAt(text, at)),\n Option.map((at) =>\n pipe(\n Option.liftPredicate(at + matchLength(ATTRIBUTE_NAME, text, at), (end) => end > at),\n Option.match({\n // Not a name: step over the character, since the tag may still go on after it.\n onNone: (): Step => [[], at + 1],\n onSome: (nameEnd) => attribute(line, text, at, nameEnd),\n }),\n ),\n ),\n )\n\n/**\n * The tag that opens a component line, read as JSX: its name, then each attribute and its\n * value, up to the `>` that closes it. Text after the tag is left alone; it is neither\n * Cosense notation nor part of the tag.\n */\nconst componentTokens = (line: number, text: string): ReadonlyArray<RawToken> =>\n pipe(\n Option.fromNullable(TAG_OPEN.exec(text)),\n Option.map(([head, lead = \"\", name = \"\"]) => [\n token(\"component\", line, lead.length, lead.length + name.length),\n ...Arr.flatten(Arr.unfold(head.length, attributeStep(line, text))),\n ]),\n Option.getOrElse(() => []),\n )\n\n// --- The page ---------------------------------------------------------------------------\n\n/** The last line of the `---` fence around YAML at the very top, if the file opens with one. */\nexport const frontmatterEnd = (lines: ReadonlyArray<string>): Option.Option<number> =>\n pipe(\n Option.liftPredicate(lines, (all) => all[0]?.trim() === \"---\"),\n Option.flatMap(Arr.findFirstIndex((line, index) => index > 0 && line.trim() === \"---\")),\n )\n\n/**\n * `frontmatterEnd`, or None when the caller reads no frontmatter: a Cosense page has none,\n * and a page whose title is `---` would otherwise lose its first lines to it.\n */\nexport const fenceOf = (\n lines: ReadonlyArray<string>,\n frontmatter: boolean,\n): Option.Option<number> => Option.flatMap(onlyIf(frontmatter, lines), frontmatterEnd)\n\n/** What the node visitor needs to know about the file around the parsed body. */\ninterface Page {\n readonly lines: ReadonlyArray<string>\n /** Lines before the body: the frontmatter, which the parser never sees. */\n readonly offset: number\n readonly components: boolean\n readonly notations: ReadonlyArray<Notation>\n}\n\n/** Each non-empty line from `first` to `last`, whole. */\nconst lineSpans = (\n type: BuiltinTokenType,\n lines: ReadonlyArray<string>,\n first: number,\n last: number,\n): ReadonlyArray<RawToken> =>\n Arr.filterMap(Arr.range(first, last), (line) =>\n pipe(\n Option.fromNullable(lines[line]),\n Option.filter((text) => text.length > 0),\n Option.map((text) => token(type, line, 0, text.length)),\n ),\n )\n\n/** The line's text, when the file reads components and the line opens with a tag. */\nconst componentLine = (page: Page, line: number): Option.Option<string> =>\n pipe(\n Option.fromNullable(page.lines[line]),\n Option.filter((text) => page.components && COMPONENT_LINE.test(text)),\n )\n\n/** The `>` and spaces that open a quote line, from its indentation onwards. */\nconst quoteToken = (page: Page, node: NodeOfType<\"line\">, line: number): Option.Option<RawToken> =>\n pipe(\n onlyIf(node.quote, page.lines[line] ?? \"\"),\n Option.map((text) => matchLength(/^[> ]*/, text, node.indent)),\n Option.filter((length) => length > 0),\n Option.map((length) => token(\"quote\", line, node.indent, node.indent + length)),\n )\n\n/**\n * A command line (`$ ls`) is code as a whole: the parser keeps its text as written, with no\n * notation in it, so its one text node is the span to colour.\n */\nconst commandTokens = (\n line: NodeOfType<\"line\">,\n shift: (position: Position) => Position,\n): ReadonlyArray<RawToken> =>\n Arr.map(line.children, (child) => spanToken(\"code\", shift(child.position)))\n\n/** A token for each of `notations` whose marker opens the decoration. */\nconst notationTokens = (\n notations: ReadonlyArray<Notation>,\n node: Decoration,\n position: Position,\n): ReadonlyArray<RawToken> =>\n Arr.filterMap(notations, ({ marker, name }) =>\n onlyIf(node.markers.includes(marker), {\n line: position.start.line,\n char: position.start.column,\n length: position.end.column - position.start.column,\n type: \"notation\" as const,\n name,\n }),\n )\n\nconst nodeTokens =\n (page: Page) =>\n (node: AnyNode): Picked<RawToken> => {\n const lineOf = (position: Position) => position.start.line + page.offset\n const shift = (position: Position): Position => ({\n start: { ...position.start, line: position.start.line + page.offset },\n end: { ...position.end, line: position.end.line + page.offset },\n })\n return Match.value(node).pipe(\n Match.discriminators(\"type\")({\n // A page that opens with a component has no title line: the tag is what it is.\n title: (title) =>\n leaf(\n pipe(\n componentLine(page, lineOf(title.position)),\n Option.match({\n onNone: () => [spanToken(\"title\", shift(title.position))],\n onSome: (text) => componentTokens(lineOf(title.position), text),\n }),\n ),\n ),\n codeBlock: (block) =>\n leaf(\n lineSpans(\n \"codeBlock\",\n page.lines,\n lineOf(block.position),\n block.position.end.line + page.offset,\n ),\n ),\n table: (table) =>\n leaf(\n lineSpans(\n \"table\",\n page.lines,\n lineOf(table.position),\n table.position.end.line + page.offset,\n ),\n ),\n // A component tag owns its line, so nothing on it is Cosense notation.\n line: (line) =>\n pipe(\n componentLine(page, lineOf(line.position)),\n Option.match({\n onSome: (text) => leaf(componentTokens(lineOf(line.position), text)),\n onNone: () =>\n line.monospace\n ? leaf(commandTokens(line, shift))\n : branch(Option.toArray(quoteToken(page, line, lineOf(line.position)))),\n }),\n ),\n // Descend: a decoration can wrap a link or an image (`[* [page]]`), and that child\n // needs its own token over the same span.\n decoration: (decoration) =>\n branch([\n ...decorationTokenTypes(decoration).map((type) =>\n spanToken(type, shift(decoration.position)),\n ),\n ...notationTokens(page.notations, decoration, shift(decoration.position)),\n ]),\n }),\n Match.orElse((other) =>\n branch(\n pipe(\n Option.fromNullable(INLINE_TOKEN_TYPE[other.type]),\n Option.map((type) => spanToken(type, shift(other.position))),\n Option.toArray,\n ),\n ),\n ),\n )\n }\n\nexport interface ComputeTokensOptions {\n /** `.csnx` also reads a line that is one component tag. `.csn` never does. */\n readonly components?: boolean\n /** How to parse: the site's own notation extensions, as its build parses with them. */\n readonly parseOptions?: ParseOptions\n /** Notations the caller adds, each sent as a token of its own name (see `Notation`). */\n readonly notations?: ReadonlyArray<Notation>\n /**\n * Whether a `---` fence on the first line opens YAML to skip (default: true). Off for\n * Cosense pages, which have no frontmatter.\n */\n readonly frontmatter?: boolean\n}\n\n/** Pure text -> tokens. The LSP delta encoding lives in encodeTokens, so this stays testable. */\nexport const computeTokens = (text: string, options: ComputeTokensOptions = {}): RawToken[] => {\n const lines = normalizeLineEndings(text).split(\"\\n\")\n const notations = options.notations ?? []\n // Frontmatter is YAML, so the Cosense parser must not see it: `---` would otherwise read\n // as a strikethrough marker and the keys as plain text on the title line.\n const fence = fenceOf(lines, options.frontmatter ?? true)\n const page: Page = {\n lines,\n offset: Option.match(fence, { onNone: () => 0, onSome: (end) => end + 1 }),\n components: options.components ?? false,\n notations,\n }\n\n const frontmatterTokens = Option.match(fence, {\n onNone: () => [],\n onSome: (end) => lineSpans(\"frontmatter\", lines, 0, end),\n })\n const body = parse(lines.slice(page.offset).join(\"\\n\"), options.parseOptions ?? {})\n\n return pipe(\n [...frontmatterTokens, ...gather(body, nodeTokens(page))],\n Arr.filter((t) => t.length > 0),\n Arr.sort(BY_POSITION),\n )\n}\n\n/** The name a token goes by: a notation's is the caller's, any other's its type. */\nconst ownName = (token: RawToken): string =>\n Match.value(token).pipe(\n Match.when({ type: \"notation\" }, (notation) => notation.name),\n Match.orElse((builtin) => builtin.type),\n )\n\n/**\n * Each token's place in `legend`: its own name when the legend has it, else the LSP type\n * its kind is drawn as. LEGEND holds none of the own names, so it always gets the LSP type.\n */\nconst legendIndex = (legend: ReadonlyArray<string>) => {\n const placeOf = (type: string): Option.Option<number> =>\n Option.liftPredicate(legend.indexOf(type), (index) => index >= 0)\n return (token: RawToken): number =>\n pipe(\n placeOf(ownName(token)),\n Option.orElse(() => placeOf(LSP_TYPE[token.type])),\n Option.getOrElse(() => 0),\n )\n}\n\n/**\n * LSP relative encoding: [deltaLine, deltaStartChar, length, tokenType, tokenModifiers]*.\n * `legend` is the one sent at initialize: LEGEND, `legendOf(notations)`, or a legend of the\n * client's own naming such as `[...TOKEN_TYPES, ...names]`.\n */\nexport const encodeTokens = (\n tokens: ReadonlyArray<RawToken>,\n legend: ReadonlyArray<string> = LEGEND,\n): number[] => {\n const indexOf = legendIndex(legend)\n return pipe(\n Arr.sort(tokens, BY_POSITION),\n Arr.mapAccum({ line: 0, char: 0 }, (previous, t) => [\n { line: t.line, char: t.char },\n [\n t.line - previous.line,\n t.line === previous.line ? t.char - previous.char : t.char,\n t.length,\n indexOf(t),\n 0,\n ],\n ]),\n ([, rows]) => Arr.flatten(rows),\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tokens.mjs","names":["Arr"],"sources":["../src/tokens.ts"],"sourcesContent":["import type {\n AnyNode,\n AnyNodeType,\n Decoration,\n NodeOfType,\n ParseOptions,\n Position,\n} from \"@cosense-toolbox/parser\"\nimport { normalizeLineEndings, parse } from \"@cosense-toolbox/parser\"\nimport { Array as Arr, Match, Option, Order, pipe } from \"effect\"\n\nimport { branch, gather, leaf, type Picked } from \"./tree\"\n\n/**\n * Cosense notation as LSP semantic tokens.\n *\n * These names are the ones this package reasons in, and they are not what goes on the wire.\n * A client announces the token types it knows at initialize and drops everything else —\n * some send the LSP's own list and nothing more — so `quote` would simply never be drawn.\n * LEGEND below is what is sent, and LSP_TYPE is the translation.\n */\nexport const TOKEN_TYPES = [\n \"title\",\n \"link\",\n \"projectLink\",\n \"externalLink\",\n \"hashtag\",\n \"code\",\n \"codeBlock\",\n \"formula\",\n \"icon\",\n \"quote\",\n \"bold\",\n \"italic\",\n \"strike\",\n \"underline\",\n \"image\",\n // A video, a sound, a map, or a player of another service embedded in the page.\n \"media\",\n \"table\",\n \"bold2\",\n \"bold3\",\n // `.csnx` only: a line that opens with a component tag. The tag's name, its attribute\n // names, and the two kinds of value, as JSX reads them.\n \"component\",\n \"attribute\",\n \"attributeValue\",\n \"expression\",\n // The YAML at the top of the file, which is not Cosense notation at all.\n \"frontmatter\",\n // A notation the caller defines (see `Notation`), such as `[! 注意]`. The token carries\n // the name the caller gave it.\n \"notation\",\n] as const\n\nexport type TokenType = (typeof TOKEN_TYPES)[number]\n\n/** Every token type but `notation`, whose tokens also carry a name. */\ntype BuiltinTokenType = Exclude<TokenType, \"notation\">\n\ninterface Span {\n readonly line: number\n readonly char: number\n readonly length: number\n}\n\nexport type RawToken =\n | (Span & { readonly type: BuiltinTokenType })\n | (Span & { readonly type: \"notation\"; readonly name: string })\n\n/**\n * A name the caller gives to a decoration marker, as `warning` to the `!` of `[! 注意]`: the\n * token type that decoration is sent as.\n *\n * The marker has to be one Cosense decorates with (`` !\"#%&'()*+,-./{|}<>_~= ``), since\n * those are the ones the parser reads as a decoration. Any other, such as `@`, leaves the\n * bracket a link, as it is in Cosense Web: how a notation looks does not change how it parses.\n */\nexport interface Notation {\n readonly marker: string\n readonly name: string\n}\n\n/**\n * The legend sent to the client: the LSP's own token types, in its own order.\n *\n * Every entry is in the set a client is expected to understand, which is what makes the\n * colours appear without the reader configuring anything.\n */\nexport const LEGEND = [\n \"namespace\",\n \"type\",\n \"function\",\n \"variable\",\n \"property\",\n \"parameter\",\n \"decorator\",\n \"macro\",\n \"keyword\",\n \"string\",\n \"number\",\n \"comment\",\n] as const\n\n/**\n * What each notation is drawn as. Chosen so that things a reader separates by eye land on\n * different entries: a link is not a tag, and a quote is not a heading. `bold` and its\n * louder siblings share one entry, because the LSP has no notion of weight and a theme\n * gives each type a single colour.\n */\nconst LSP_TYPE: Record<TokenType, (typeof LEGEND)[number]> = {\n title: \"namespace\",\n link: \"function\",\n projectLink: \"function\",\n externalLink: \"string\",\n icon: \"function\",\n image: \"string\",\n media: \"string\",\n hashtag: \"decorator\",\n code: \"string\",\n codeBlock: \"string\",\n formula: \"number\",\n table: \"property\",\n quote: \"comment\",\n frontmatter: \"comment\",\n component: \"type\",\n attribute: \"parameter\",\n attributeValue: \"string\",\n expression: \"variable\",\n bold: \"keyword\",\n bold2: \"keyword\",\n bold3: \"keyword\",\n italic: \"macro\",\n strike: \"comment\",\n underline: \"variable\",\n // What a caller's notation is sent as when the legend has no type of its name.\n notation: \"decorator\",\n}\n\n/**\n * LEGEND with the names of `notations` after it, each once: the legend for a client that\n * accepts token types of the caller's own naming. The LSP's types keep their places.\n *\n * A client that colours by this package's own names instead passes those to `encodeTokens`\n * (`[...TOKEN_TYPES, ...names]`): a token whose own name is in the legend is sent as that.\n */\nexport const legendOf = (notations: ReadonlyArray<Notation>): ReadonlyArray<string> =>\n Arr.dedupe([...LEGEND, ...Arr.map(notations, (notation) => notation.name)])\n\n/** Leaf inline nodes that map 1:1. Each sits on one line, so no splitting is needed. */\nconst INLINE_TOKEN_TYPE: Partial<Record<AnyNodeType, BuiltinTokenType>> = {\n internalLink: \"link\",\n externalLink: \"externalLink\",\n projectLink: \"projectLink\",\n hashtag: \"hashtag\",\n inlineCode: \"code\",\n image: \"image\",\n video: \"media\",\n audio: \"media\",\n embed: \"media\",\n location: \"media\",\n icon: \"icon\",\n formula: \"formula\",\n}\n\n/** Top to bottom, then left to right: the order the LSP encoding needs. */\nconst BY_POSITION: Order.Order<RawToken> = Order.combine(\n Order.mapInput(Order.number, (token: RawToken) => token.line),\n Order.mapInput(Order.number, (token: RawToken) => token.char),\n)\n\nconst onlyIf = <A>(condition: boolean, value: A): Option.Option<A> =>\n condition ? Option.some(value) : Option.none()\n\n/** A token over `[from, to)` of one line. */\nconst token = (type: BuiltinTokenType, line: number, from: number, to: number): RawToken => ({\n line,\n char: from,\n length: to - from,\n type,\n})\n\nconst spanToken = (type: BuiltinTokenType, position: Position): RawToken =>\n token(type, position.start.line, position.start.column, position.end.column)\n\n/** How many characters from `from` the `^`-anchored `pattern` takes; 0 when it takes none. */\nconst matchLength = (pattern: RegExp, text: string, from: number): number =>\n pipe(\n Option.fromNullable(pattern.exec(text.slice(from))),\n Option.match({ onNone: () => 0, onSome: ([matched]) => matched.length }),\n )\n\n// --- Emphasis ---------------------------------------------------------------------------\n\n/** Cosense sizes emphasis by asterisk count (`[*]`..`[*****]`); sizeLevel is 0-indexed. */\nconst boldTokenType = (sizeLevel: number): BuiltinTokenType =>\n Match.value(sizeLevel).pipe(\n Match.when(\n (level) => level >= 2,\n (): BuiltinTokenType => \"bold3\",\n ),\n Match.when(1, (): BuiltinTokenType => \"bold2\"),\n Match.orElse((): BuiltinTokenType => \"bold\"),\n )\n\n/** Every decoration a run carries, since Cosense applies all of them: `[-* x]` is both. */\nconst decorationTokenTypes = (node: Decoration): ReadonlyArray<BuiltinTokenType> =>\n Arr.getSomes([\n onlyIf(node.underline, \"underline\" as const),\n onlyIf(node.strike, \"strike\" as const),\n onlyIf(node.italic, \"italic\" as const),\n onlyIf(node.bold, boldTokenType(node.sizeLevel)),\n ])\n\n// --- Component lines (`.csnx`) ----------------------------------------------------------\n\nexport const COMPONENT_LINE = /^\\s*<\\/?[A-Z][A-Za-z0-9_.]*(\\s|\\/?>)/\nconst TAG_OPEN = /^(\\s*<\\/?)([A-Z][A-Za-z0-9_.]*)/\nconst ATTRIBUTE_NAME = /^[A-Za-z_:][A-Za-z0-9_:.-]*/\nconst SPACE = /^\\s*/\n\n/** Where a quoted value opened at `from` ends: after its closing quote, or at the line's end. */\nconst quotedEnd = (text: string, from: number): number =>\n pipe(\n text.indexOf(text.charAt(from), from + 1),\n Option.liftPredicate((close) => close >= 0),\n Option.match({ onNone: () => text.length, onSome: (close) => close + 1 }),\n )\n\n/** Where `{ ... }` opened at `from` ends, counting nested braces: after its `}`, or the line's end. */\nconst bracedEnd = (text: string, from: number): number =>\n pipe(\n text.slice(from).split(\"\"),\n Arr.scan(0, (depth, char) => (char === \"{\" ? depth + 1 : char === \"}\" ? depth - 1 : depth)),\n // scan starts with the depth before any character; the rest line up with the characters.\n Arr.drop(1),\n Arr.findFirstIndex((depth) => depth === 0),\n Option.match({ onNone: () => text.length, onSome: (index) => from + index + 1 }),\n )\n\ntype ValueType = \"attributeValue\" | \"expression\"\n\n/** The attribute value that starts at `from`: its type and where it ends. */\nconst valueAt = (text: string, from: number): Option.Option<readonly [ValueType, number]> =>\n Match.value(text.charAt(from)).pipe(\n Match.when(\n (char) => char === '\"' || char === \"'\",\n () => Option.some([\"attributeValue\", quotedEnd(text, from)] as const),\n ),\n Match.when(\"{\", () => Option.some([\"expression\", bracedEnd(text, from)] as const)),\n Match.orElse(() => Option.none()),\n )\n\n/** The tokens of one attribute, and where the next may start. */\ntype Step = readonly [ReadonlyArray<RawToken>, number]\n\n/** `at` past any whitespace. */\nconst skipSpace = (text: string, at: number): number => at + matchLength(SPACE, text, at)\n\n/** Whether the tag closes at `at`, with `>` or `/>`, or the line ends there. */\nconst closesAt = (text: string, at: number): boolean =>\n at >= text.length || text.startsWith(\">\", at) || text.startsWith(\"/>\", at)\n\n/**\n * An attribute named over `[at, nameEnd)`: the name, then `=` and a value if one follows.\n * A name with no `=` after it stands alone, as JSX reads `<Dialog open>`.\n */\nconst attribute = (line: number, text: string, at: number, nameEnd: number): Step => {\n const name = token(\"attribute\", line, at, nameEnd)\n const afterName = skipSpace(text, nameEnd)\n return pipe(\n Option.liftPredicate(afterName, (equals) => text.charAt(equals) === \"=\"),\n Option.map((equals) => skipSpace(text, equals + 1)),\n Option.match({\n onNone: (): Step => [[name], afterName],\n onSome: (valueStart): Step =>\n pipe(\n valueAt(text, valueStart),\n Option.match({\n onNone: (): Step => [[name], valueStart],\n onSome: ([type, end]): Step => [[name, token(type, line, valueStart, end)], end],\n }),\n ),\n }),\n )\n}\n\n/** One attribute from `from`, or None once the tag has closed. */\nconst attributeStep =\n (line: number, text: string) =>\n (from: number): Option.Option<Step> =>\n pipe(\n Option.liftPredicate(skipSpace(text, from), (at) => !closesAt(text, at)),\n Option.map((at) =>\n pipe(\n Option.liftPredicate(at + matchLength(ATTRIBUTE_NAME, text, at), (end) => end > at),\n Option.match({\n // Not a name: step over the character, since the tag may still go on after it.\n onNone: (): Step => [[], at + 1],\n onSome: (nameEnd) => attribute(line, text, at, nameEnd),\n }),\n ),\n ),\n )\n\n/**\n * The tag that opens a component line, read as JSX: its name, then each attribute and its\n * value, up to the `>` that closes it. Text after the tag is left alone; it is neither\n * Cosense notation nor part of the tag.\n */\nconst componentTokens = (line: number, text: string): ReadonlyArray<RawToken> =>\n pipe(\n Option.fromNullable(TAG_OPEN.exec(text)),\n Option.map(([head, lead = \"\", name = \"\"]) => [\n token(\"component\", line, lead.length, lead.length + name.length),\n ...Arr.flatten(Arr.unfold(head.length, attributeStep(line, text))),\n ]),\n Option.getOrElse(() => []),\n )\n\n// --- The page ---------------------------------------------------------------------------\n\n/** The last line of the `---` fence around YAML at the very top, if the file opens with one. */\nexport const frontmatterEnd = (lines: ReadonlyArray<string>): Option.Option<number> =>\n pipe(\n Option.liftPredicate(lines, (all) => all[0]?.trim() === \"---\"),\n Option.flatMap(Arr.findFirstIndex((line, index) => index > 0 && line.trim() === \"---\")),\n )\n\n/**\n * `frontmatterEnd`, or None when the caller reads no frontmatter: a Cosense page has none,\n * and a page whose title is `---` would otherwise lose its first lines to it.\n */\nexport const fenceOf = (\n lines: ReadonlyArray<string>,\n frontmatter: boolean,\n): Option.Option<number> => Option.flatMap(onlyIf(frontmatter, lines), frontmatterEnd)\n\n/** What the node visitor needs to know about the file around the parsed body. */\ninterface Page {\n readonly lines: ReadonlyArray<string>\n /** Lines before the body: the frontmatter, which the parser never sees. */\n readonly offset: number\n readonly components: boolean\n readonly notations: ReadonlyArray<Notation>\n}\n\n/** Each non-empty line from `first` to `last`, whole. */\nconst lineSpans = (\n type: BuiltinTokenType,\n lines: ReadonlyArray<string>,\n first: number,\n last: number,\n): ReadonlyArray<RawToken> =>\n Arr.filterMap(Arr.range(first, last), (line) =>\n pipe(\n Option.fromNullable(lines[line]),\n Option.filter((text) => text.length > 0),\n Option.map((text) => token(type, line, 0, text.length)),\n ),\n )\n\n/** The line's text, when the file reads components and the line opens with a tag. */\nconst componentLine = (page: Page, line: number): Option.Option<string> =>\n pipe(\n Option.fromNullable(page.lines[line]),\n Option.filter((text) => page.components && COMPONENT_LINE.test(text)),\n )\n\n/** The `>` and spaces that open a quote line, from its indentation onwards. */\nconst quoteToken = (page: Page, node: NodeOfType<\"line\">, line: number): Option.Option<RawToken> =>\n pipe(\n onlyIf(node.quote, page.lines[line] ?? \"\"),\n Option.map((text) => matchLength(/^[> ]*/, text, node.indent)),\n Option.filter((length) => length > 0),\n Option.map((length) => token(\"quote\", line, node.indent, node.indent + length)),\n )\n\n/**\n * A command line (`$ ls`) is code as a whole: the parser keeps its text as written, with no\n * notation in it, so its one text node is the span to colour.\n */\nconst commandTokens = (\n line: NodeOfType<\"line\">,\n shift: (position: Position) => Position,\n): ReadonlyArray<RawToken> =>\n Arr.map(line.children, (child) => spanToken(\"code\", shift(child.position)))\n\n/** A token for each of `notations` whose marker opens the decoration. */\nconst notationTokens = (\n notations: ReadonlyArray<Notation>,\n node: Decoration,\n position: Position,\n): ReadonlyArray<RawToken> =>\n Arr.filterMap(notations, ({ marker, name }) =>\n onlyIf(node.markers.includes(marker), {\n line: position.start.line,\n char: position.start.column,\n length: position.end.column - position.start.column,\n type: \"notation\" as const,\n name,\n }),\n )\n\nconst nodeTokens =\n (page: Page) =>\n (node: AnyNode): Picked<RawToken> => {\n const lineOf = (position: Position) => position.start.line + page.offset\n const shift = (position: Position): Position => ({\n start: { ...position.start, line: position.start.line + page.offset },\n end: { ...position.end, line: position.end.line + page.offset },\n })\n return Match.value(node).pipe(\n Match.discriminators(\"type\")({\n // A page that opens with a component has no title line: the tag is what it is.\n title: (title) =>\n leaf(\n pipe(\n componentLine(page, lineOf(title.position)),\n Option.match({\n onNone: () => [spanToken(\"title\", shift(title.position))],\n onSome: (text) => componentTokens(lineOf(title.position), text),\n }),\n ),\n ),\n codeBlock: (block) =>\n leaf(\n lineSpans(\n \"codeBlock\",\n page.lines,\n lineOf(block.position),\n block.position.end.line + page.offset,\n ),\n ),\n table: (table) =>\n leaf(\n lineSpans(\n \"table\",\n page.lines,\n lineOf(table.position),\n table.position.end.line + page.offset,\n ),\n ),\n // A component tag owns its line, so nothing on it is Cosense notation.\n line: (line) =>\n pipe(\n componentLine(page, lineOf(line.position)),\n Option.match({\n onSome: (text) => leaf(componentTokens(lineOf(line.position), text)),\n onNone: () =>\n line.monospace\n ? leaf(commandTokens(line, shift))\n : branch(Option.toArray(quoteToken(page, line, lineOf(line.position)))),\n }),\n ),\n // Descend: a decoration can wrap a link or an image (`[* [page]]`), and that child\n // needs its own token over the same span.\n decoration: (decoration) =>\n branch([\n ...decorationTokenTypes(decoration).map((type) =>\n spanToken(type, shift(decoration.position)),\n ),\n ...notationTokens(page.notations, decoration, shift(decoration.position)),\n ]),\n }),\n Match.orElse((other) =>\n branch(\n pipe(\n Option.fromNullable(INLINE_TOKEN_TYPE[other.type]),\n Option.map((type) => spanToken(type, shift(other.position))),\n Option.toArray,\n ),\n ),\n ),\n )\n }\n\nexport interface ComputeTokensOptions {\n /** `.csnx` also reads a line that is one component tag. `.csn` never does. */\n readonly components?: boolean\n /** How to parse: the site's own notation extensions, as its build parses with them. */\n readonly parseOptions?: ParseOptions\n /** Notations the caller adds, each sent as a token of its own name (see `Notation`). */\n readonly notations?: ReadonlyArray<Notation>\n /**\n * Whether a `---` fence on the first line opens YAML to skip (default: true). Off for\n * Cosense pages, which have no frontmatter.\n */\n readonly frontmatter?: boolean\n}\n\n/** Pure text -> tokens. The LSP delta encoding lives in encodeTokens, so this stays testable. */\nexport const computeTokens = (text: string, options: ComputeTokensOptions = {}): RawToken[] => {\n const lines = normalizeLineEndings(text).split(\"\\n\")\n const notations = options.notations ?? []\n // Frontmatter is YAML, so the Cosense parser must not see it: `---` would otherwise read\n // as a strikethrough marker and the keys as plain text on the title line.\n const fence = fenceOf(lines, options.frontmatter ?? true)\n const page: Page = {\n lines,\n offset: Option.match(fence, { onNone: () => 0, onSome: (end) => end + 1 }),\n components: options.components ?? false,\n notations,\n }\n\n const frontmatterTokens = Option.match(fence, {\n onNone: () => [],\n onSome: (end) => lineSpans(\"frontmatter\", lines, 0, end),\n })\n const body = parse(lines.slice(page.offset).join(\"\\n\"), options.parseOptions ?? {})\n\n return pipe(\n [...frontmatterTokens, ...gather(body, nodeTokens(page))],\n Arr.filter((t) => t.length > 0),\n Arr.sort(BY_POSITION),\n )\n}\n\n/** The name a token goes by: a notation's is the caller's, any other's its type. */\nconst ownName = (token: RawToken): string =>\n Match.value(token).pipe(\n Match.when({ type: \"notation\" }, (notation) => notation.name),\n Match.orElse((builtin) => builtin.type),\n )\n\n/**\n * Each token's place in `legend`: its own name when the legend has it, else the LSP type\n * its kind is drawn as. LEGEND holds none of the own names, so it always gets the LSP type.\n */\nconst legendIndex = (legend: ReadonlyArray<string>) => {\n const placeOf = (type: string): Option.Option<number> =>\n Option.liftPredicate(legend.indexOf(type), (index) => index >= 0)\n return (token: RawToken): number =>\n pipe(\n placeOf(ownName(token)),\n Option.orElse(() => placeOf(LSP_TYPE[token.type])),\n Option.getOrElse(() => 0),\n )\n}\n\n/**\n * LSP relative encoding: [deltaLine, deltaStartChar, length, tokenType, tokenModifiers]*.\n * `legend` is the one sent at initialize: LEGEND, `legendOf(notations)`, or a legend of the\n * client's own naming such as `[...TOKEN_TYPES, ...names]`.\n */\nexport const encodeTokens = (\n tokens: ReadonlyArray<RawToken>,\n legend: ReadonlyArray<string> = LEGEND,\n): number[] => {\n const indexOf = legendIndex(legend)\n return pipe(\n Arr.sort(tokens, BY_POSITION),\n Arr.mapAccum({ line: 0, char: 0 }, (previous, t) => [\n { line: t.line, char: t.char },\n [\n t.line - previous.line,\n t.line === previous.line ? t.char - previous.char : t.char,\n t.length,\n indexOf(t),\n 0,\n ],\n ]),\n ([, rows]) => Arr.flatten(rows),\n )\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosense-toolbox/lsp",
3
- "version": "0.1.0-beta.6",
3
+ "version": "0.1.0-beta.7",
4
4
  "type": "module",
5
5
  "description": "Language server for .csn and .csnx, and a check of a whole site's links: csn-lsp.",
6
6
  "license": "MIT",
@@ -51,7 +51,7 @@
51
51
  "prepublishOnly": "bun run build"
52
52
  },
53
53
  "dependencies": {
54
- "@cosense-toolbox/parser": "0.1.0-beta.6",
54
+ "@cosense-toolbox/parser": "0.1.0-beta.7",
55
55
  "effect": "^3.22.1",
56
56
  "vscode-languageserver": "^10.1.0",
57
57
  "vscode-languageserver-textdocument": "^1.0.12"