deploy: v0.12 fix 存档key名

This commit is contained in:
2026-06-24 03:09:02 +00:00
parent 7a08d49fe3
commit e0a5281119
58743 changed files with 7297269 additions and 39505 deletions
+16
View File
@@ -0,0 +1,16 @@
name: Trigger CI
on: push
jobs:
build:
name: Dispatch to main repo
runs-on: ubuntu-latest
steps:
- name: Emit repository_dispatch
uses: mvasigh/dispatch-action@main
with:
# You should create a personal access token and store it in your repository
token: ${{ secrets.DISPATCH_AUTH }}
repo: dev
owner: codemirror
event_type: push
+72
View File
@@ -0,0 +1,72 @@
## 6.1.0 (2024-03-06)
### New features
Add an `autoCloseTags` extension that closes tags on typing > or /. Enable it by default in the `xml()` language support.
## 6.0.2 (2023-01-12)
### Bug fixes
Use only the tag name for matching of opening and closing tags.
## 6.0.1 (2022-10-24)
### Bug fixes
Make sure the language object has a name.
## 6.0.0 (2022-06-08)
### Breaking changes
Update dependencies to 6.0.0
## 0.20.0 (2022-04-20)
### Breaking changes
Update dependencies to 0.20.0
## 0.19.2 (2021-09-23)
### New features
Use more specific highlighting tags for attribute names and values.
## 0.19.1 (2021-08-11)
### Bug fixes
Fix incorrect versions for @lezer dependencies.
## 0.19.0 (2021-08-11)
### Breaking changes
Update dependencies to 0.19.0
## 0.18.0 (2021-03-03)
### Breaking changes
Update dependencies to 0.18.
## 0.17.2 (2021-02-12)
### Bug fixes
Export configuration types and `completeFromSchema` function (which were previously accidentally unexported).
## 0.17.1 (2021-01-06)
### New features
The package now also exports a CommonJS module.
## 0.17.0 (2020-12-29)
### Breaking changes
First numbered release.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+137
View File
@@ -0,0 +1,137 @@
<!-- NOTE: README.md is generated from src/README.md -->
# @codemirror/lang-xml [![NPM version](https://img.shields.io/npm/v/@codemirror/lang-xml.svg)](https://www.npmjs.org/package/@codemirror/lang-xml)
[ [**WEBSITE**](https://codemirror.net/) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/lang-xml/blob/main/CHANGELOG.md) ]
This package implements XML language support for the
[CodeMirror](https://codemirror.net/) code editor.
The [project page](https://codemirror.net/) has more information, a
number of [examples](https://codemirror.net/examples/) and the
[documentation](https://codemirror.net/docs/).
This code is released under an
[MIT license](https://github.com/codemirror/lang-xml/tree/main/LICENSE).
We aim to be an inclusive, welcoming community. To make that explicit,
we have a [code of
conduct](http://contributor-covenant.org/version/1/1/0/) that applies
to communication around the project.
## API Reference
<dl>
<dt id="user-content-xml">
<code><strong><a href="#user-content-xml">xml</a></strong>(<a id="user-content-xml^conf" href="#user-content-xml^conf">conf</a>&#8288;?: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">Object</a> = {}) → <a href="https://codemirror.net/docs/ref#language.LanguageSupport">LanguageSupport</a></code></dt>
<dd><p>XML language support. Includes schema-based autocompletion when
configured.</p>
<dl><dt id="user-content-xml^conf">
<code><strong><a href="#user-content-xml^conf">conf</a></strong></code></dt>
<dd><dl><dt id="user-content-xml^conf.elements">
<code><strong><a href="#user-content-xml^conf.elements">elements</a></strong>&#8288;?: readonly <a href="#user-content-elementspec">ElementSpec</a>[]</code></dt>
<dd><p>Provide a schema to create completions from.</p>
</dd><dt id="user-content-xml^conf.attributes">
<code><strong><a href="#user-content-xml^conf.attributes">attributes</a></strong>&#8288;?: readonly <a href="#user-content-attrspec">AttrSpec</a>[]</code></dt>
<dd><p>Supporting attribute descriptions for the schema specified in
<a href="#user-content-xml%5econf.elements"><code>elements</code></a>.</p>
</dd><dt id="user-content-xml^conf.autoclosetags">
<code><strong><a href="#user-content-xml^conf.autoclosetags">autoCloseTags</a></strong>&#8288;?: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></code></dt>
<dd><p>Determines whether <a href="#user-content-autoclosetags"><code>autoCloseTags</code></a>
is included in the support extensions. Defaults to true.</p>
</dd></dl></dd></dl></dd>
<dt id="user-content-xmllanguage">
<code><strong><a href="#user-content-xmllanguage">xmlLanguage</a></strong>: <a href="https://codemirror.net/docs/ref#language.LRLanguage">LRLanguage</a></code></dt>
<dd><p>A language provider based on the <a href="https://github.com/lezer-parser/xml">Lezer XML
parser</a>, extended with
highlighting and indentation information.</p>
</dd>
<dt id="user-content-elementspec">
<h4>
<code>interface</code>
<a href="#user-content-elementspec">ElementSpec</a></h4>
</dt>
<dd><p>Describes an element in your XML document schema.</p>
<dl><dt id="user-content-elementspec.name">
<code><strong><a href="#user-content-elementspec.name">name</a></strong>: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></code></dt>
<dd><p>The element name.</p>
</dd><dt id="user-content-elementspec.children">
<code><strong><a href="#user-content-elementspec.children">children</a></strong>&#8288;?: readonly <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a>[]</code></dt>
<dd><p>Allowed children in this element. When not given, all elements
are allowed inside it.</p>
</dd><dt id="user-content-elementspec.textcontent">
<code><strong><a href="#user-content-elementspec.textcontent">textContent</a></strong>&#8288;?: readonly <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a>[]</code></dt>
<dd><p>When given, allows users to complete the given content strings
as plain text when at the start of the element.</p>
</dd><dt id="user-content-elementspec.top">
<code><strong><a href="#user-content-elementspec.top">top</a></strong>&#8288;?: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></code></dt>
<dd><p>Whether this element may appear at the top of the document.</p>
</dd><dt id="user-content-elementspec.attributes">
<code><strong><a href="#user-content-elementspec.attributes">attributes</a></strong>&#8288;?: readonly (<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a> | <a href="#user-content-attrspec">AttrSpec</a>)[]</code></dt>
<dd><p>Allowed attributes in this element. Strings refer to attributes
specified in <a href="#user-content-xmlconfig.attrs"><code>XMLConfig.attrs</code></a>, but
you can also provide one-off <a href="#user-content-attrspec">attribute
specs</a>. Attributes marked as
<a href="#user-content-attrspec.global"><code>global</code></a> are allowed in every
element, and don't have to be mentioned here.</p>
</dd><dt id="user-content-elementspec.completion">
<code><strong><a href="#user-content-elementspec.completion">completion</a></strong>&#8288;?: <a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype">Partial</a>&lt;<a href="https://codemirror.net/docs/ref#autocomplete.Completion">Completion</a>&gt;</code></dt>
<dd><p>Can be provided to add extra fields to the
<a href="#user-content-autocompletion.completion">completion</a> object created for this
element.</p>
</dd></dl>
</dd>
<dt id="user-content-attrspec">
<h4>
<code>interface</code>
<a href="#user-content-attrspec">AttrSpec</a></h4>
</dt>
<dd><p>Describes an attribute in your XML schema.</p>
<dl><dt id="user-content-attrspec.name">
<code><strong><a href="#user-content-attrspec.name">name</a></strong>: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></code></dt>
<dd><p>The attribute name.</p>
</dd><dt id="user-content-attrspec.values">
<code><strong><a href="#user-content-attrspec.values">values</a></strong>&#8288;?: readonly (<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a> | <a href="https://codemirror.net/docs/ref#autocomplete.Completion">Completion</a>)[]</code></dt>
<dd><p>Pre-defined values to complete for this attribute.</p>
</dd><dt id="user-content-attrspec.global">
<code><strong><a href="#user-content-attrspec.global">global</a></strong>&#8288;?: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></code></dt>
<dd><p>When <code>true</code>, this attribute can be added to all elements.</p>
</dd><dt id="user-content-attrspec.completion">
<code><strong><a href="#user-content-attrspec.completion">completion</a></strong>&#8288;?: <a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype">Partial</a>&lt;<a href="https://codemirror.net/docs/ref#autocomplete.Completion">Completion</a>&gt;</code></dt>
<dd><p>Provides extra fields to the
<a href="#user-content-autocompletion.completion">completion</a> object created for this
element</p>
</dd></dl>
</dd>
<dt id="user-content-completefromschema">
<code><strong><a href="#user-content-completefromschema">completeFromSchema</a></strong>(<a id="user-content-completefromschema^eltspecs" href="#user-content-completefromschema^eltspecs">eltSpecs</a>: readonly <a href="#user-content-elementspec">ElementSpec</a>[], <a id="user-content-completefromschema^attrspecs" href="#user-content-completefromschema^attrspecs">attrSpecs</a>: readonly <a href="#user-content-attrspec">AttrSpec</a>[]) → <a href="https://codemirror.net/docs/ref#autocomplete.CompletionSource">CompletionSource</a></code></dt>
<dd><p>Create a completion source for the given schema.</p>
</dd>
<dt id="user-content-autoclosetags">
<code><strong><a href="#user-content-autoclosetags">autoCloseTags</a></strong>: <a href="https://codemirror.net/docs/ref#state.Extension">Extension</a></code></dt>
<dd><p>Extension that will automatically insert close tags when a <code>&gt;</code> or
<code>/</code> is typed.</p>
</dd>
</dl>
+314
View File
@@ -0,0 +1,314 @@
'use strict';
var xml$1 = require('@lezer/xml');
var language = require('@codemirror/language');
var state = require('@codemirror/state');
var view = require('@codemirror/view');
function tagName(doc, tag) {
let name = tag && tag.getChild("TagName");
return name ? doc.sliceString(name.from, name.to) : "";
}
function elementName$1(doc, tree) {
let tag = tree && tree.firstChild;
return !tag || tag.name != "OpenTag" ? "" : tagName(doc, tag);
}
function attrName(doc, tag, pos) {
let attr = tag && tag.getChildren("Attribute").find(a => a.from <= pos && a.to >= pos);
let name = attr && attr.getChild("AttributeName");
return name ? doc.sliceString(name.from, name.to) : "";
}
function findParentElement(tree) {
for (let cur = tree && tree.parent; cur; cur = cur.parent)
if (cur.name == "Element")
return cur;
return null;
}
function findLocation(state, pos) {
var _a;
let at = language.syntaxTree(state).resolveInner(pos, -1), inTag = null;
for (let cur = at; !inTag && cur.parent; cur = cur.parent)
if (cur.name == "OpenTag" || cur.name == "CloseTag" || cur.name == "SelfClosingTag" || cur.name == "MismatchedCloseTag")
inTag = cur;
if (inTag && (inTag.to > pos || inTag.lastChild.type.isError)) {
let elt = inTag.parent;
if (at.name == "TagName")
return inTag.name == "CloseTag" || inTag.name == "MismatchedCloseTag"
? { type: "closeTag", from: at.from, context: elt }
: { type: "openTag", from: at.from, context: findParentElement(elt) };
if (at.name == "AttributeName")
return { type: "attrName", from: at.from, context: inTag };
if (at.name == "AttributeValue")
return { type: "attrValue", from: at.from, context: inTag };
let before = at == inTag || at.name == "Attribute" ? at.childBefore(pos) : at;
if ((before === null || before === void 0 ? void 0 : before.name) == "StartTag")
return { type: "openTag", from: pos, context: findParentElement(elt) };
if ((before === null || before === void 0 ? void 0 : before.name) == "StartCloseTag" && before.to <= pos)
return { type: "closeTag", from: pos, context: elt };
if ((before === null || before === void 0 ? void 0 : before.name) == "Is")
return { type: "attrValue", from: pos, context: inTag };
if (before)
return { type: "attrName", from: pos, context: inTag };
return null;
}
else if (at.name == "StartCloseTag") {
return { type: "closeTag", from: pos, context: at.parent };
}
while (at.parent && at.to == pos && !((_a = at.lastChild) === null || _a === void 0 ? void 0 : _a.type.isError))
at = at.parent;
if (at.name == "Element" || at.name == "Text" || at.name == "Document")
return { type: "tag", from: pos, context: at.name == "Element" ? at : findParentElement(at) };
return null;
}
class Element {
constructor(spec, attrs, attrValues) {
this.attrs = attrs;
this.attrValues = attrValues;
this.children = [];
this.name = spec.name;
this.completion = Object.assign(Object.assign({ type: "type" }, spec.completion || {}), { label: this.name });
this.openCompletion = Object.assign(Object.assign({}, this.completion), { label: "<" + this.name });
this.closeCompletion = Object.assign(Object.assign({}, this.completion), { label: "</" + this.name + ">", boost: 2 });
this.closeNameCompletion = Object.assign(Object.assign({}, this.completion), { label: this.name + ">" });
this.text = spec.textContent ? spec.textContent.map(s => ({ label: s, type: "text" })) : [];
}
}
const Identifier = /^[:\-\.\w\u00b7-\uffff]*$/;
function attrCompletion(spec) {
return Object.assign(Object.assign({ type: "property" }, spec.completion || {}), { label: spec.name });
}
function valueCompletion(spec) {
return typeof spec == "string" ? { label: `"${spec}"`, type: "constant" }
: /^"/.test(spec.label) ? spec
: Object.assign(Object.assign({}, spec), { label: `"${spec.label}"` });
}
/**
Create a completion source for the given schema.
*/
function completeFromSchema(eltSpecs, attrSpecs) {
let allAttrs = [], globalAttrs = [];
let attrValues = Object.create(null);
for (let s of attrSpecs) {
let completion = attrCompletion(s);
allAttrs.push(completion);
if (s.global)
globalAttrs.push(completion);
if (s.values)
attrValues[s.name] = s.values.map(valueCompletion);
}
let allElements = [], topElements = [];
let byName = Object.create(null);
for (let s of eltSpecs) {
let attrs = globalAttrs, attrVals = attrValues;
if (s.attributes)
attrs = attrs.concat(s.attributes.map(s => {
if (typeof s == "string")
return allAttrs.find(a => a.label == s) || { label: s, type: "property" };
if (s.values) {
if (attrVals == attrValues)
attrVals = Object.create(attrVals);
attrVals[s.name] = s.values.map(valueCompletion);
}
return attrCompletion(s);
}));
let elt = new Element(s, attrs, attrVals);
byName[elt.name] = elt;
allElements.push(elt);
if (s.top)
topElements.push(elt);
}
if (!topElements.length)
topElements = allElements;
for (let i = 0; i < allElements.length; i++) {
let s = eltSpecs[i], elt = allElements[i];
if (s.children) {
for (let ch of s.children)
if (byName[ch])
elt.children.push(byName[ch]);
}
else {
elt.children = allElements;
}
}
return cx => {
var _a;
let { doc } = cx.state, loc = findLocation(cx.state, cx.pos);
if (!loc || (loc.type == "tag" && !cx.explicit))
return null;
let { type, from, context } = loc;
if (type == "openTag") {
let children = topElements;
let parentName = elementName$1(doc, context);
if (parentName) {
let parent = byName[parentName];
children = (parent === null || parent === void 0 ? void 0 : parent.children) || allElements;
}
return {
from,
options: children.map(ch => ch.completion),
validFor: Identifier
};
}
else if (type == "closeTag") {
let parentName = elementName$1(doc, context);
return parentName ? {
from,
to: cx.pos + (doc.sliceString(cx.pos, cx.pos + 1) == ">" ? 1 : 0),
options: [((_a = byName[parentName]) === null || _a === void 0 ? void 0 : _a.closeNameCompletion) || { label: parentName + ">", type: "type" }],
validFor: Identifier
} : null;
}
else if (type == "attrName") {
let parent = byName[tagName(doc, context)];
return {
from,
options: (parent === null || parent === void 0 ? void 0 : parent.attrs) || globalAttrs,
validFor: Identifier
};
}
else if (type == "attrValue") {
let attr = attrName(doc, context, from);
if (!attr)
return null;
let parent = byName[tagName(doc, context)];
let values = ((parent === null || parent === void 0 ? void 0 : parent.attrValues) || attrValues)[attr];
if (!values || !values.length)
return null;
return {
from,
to: cx.pos + (doc.sliceString(cx.pos, cx.pos + 1) == '"' ? 1 : 0),
options: values,
validFor: /^"[^"]*"?$/
};
}
else if (type == "tag") {
let parentName = elementName$1(doc, context), parent = byName[parentName];
let closing = [], last = context && context.lastChild;
if (parentName && (!last || last.name != "CloseTag" || tagName(doc, last) != parentName))
closing.push(parent ? parent.closeCompletion : { label: "</" + parentName + ">", type: "type", boost: 2 });
let options = closing.concat(((parent === null || parent === void 0 ? void 0 : parent.children) || (context ? allElements : topElements)).map(e => e.openCompletion));
if (context && (parent === null || parent === void 0 ? void 0 : parent.text.length)) {
let openTag = context.firstChild;
if (openTag.to > cx.pos - 20 && !/\S/.test(cx.state.sliceDoc(openTag.to, cx.pos)))
options = options.concat(parent.text);
}
return {
from,
options,
validFor: /^<\/?[:\-\.\w\u00b7-\uffff]*$/
};
}
else {
return null;
}
};
}
/**
A language provider based on the [Lezer XML
parser](https://github.com/lezer-parser/xml), extended with
highlighting and indentation information.
*/
const xmlLanguage = language.LRLanguage.define({
name: "xml",
parser: xml$1.parser.configure({
props: [
language.indentNodeProp.add({
Element(context) {
let closed = /^\s*<\//.test(context.textAfter);
return context.lineIndent(context.node.from) + (closed ? 0 : context.unit);
},
"OpenTag CloseTag SelfClosingTag"(context) {
return context.column(context.node.from) + context.unit;
}
}),
language.foldNodeProp.add({
Element(subtree) {
let first = subtree.firstChild, last = subtree.lastChild;
if (!first || first.name != "OpenTag")
return null;
return { from: first.to, to: last.name == "CloseTag" ? last.from : subtree.to };
}
}),
language.bracketMatchingHandle.add({
"OpenTag CloseTag": node => node.getChild("TagName")
})
]
}),
languageData: {
commentTokens: { block: { open: "<!--", close: "-->" } },
indentOnInput: /^\s*<\/$/
}
});
/**
XML language support. Includes schema-based autocompletion when
configured.
*/
function xml(conf = {}) {
let support = [xmlLanguage.data.of({
autocomplete: completeFromSchema(conf.elements || [], conf.attributes || [])
})];
if (conf.autoCloseTags !== false)
support.push(autoCloseTags);
return new language.LanguageSupport(xmlLanguage, support);
}
function elementName(doc, tree, max = doc.length) {
if (!tree)
return "";
let tag = tree.firstChild;
let name = tag && tag.getChild("TagName");
return name ? doc.sliceString(name.from, Math.min(name.to, max)) : "";
}
/**
Extension that will automatically insert close tags when a `>` or
`/` is typed.
*/
const autoCloseTags = view.EditorView.inputHandler.of((view, from, to, text, insertTransaction) => {
if (view.composing || view.state.readOnly || from != to || (text != ">" && text != "/") ||
!xmlLanguage.isActiveAt(view.state, from, -1))
return false;
let base = insertTransaction(), { state: state$1 } = base;
let closeTags = state$1.changeByRange(range => {
var _a, _b, _c;
let { head } = range;
let didType = state$1.doc.sliceString(head - 1, head) == text;
let after = language.syntaxTree(state$1).resolveInner(head, -1), name;
if (didType && text == ">" && after.name == "EndTag") {
let tag = after.parent;
if (((_b = (_a = tag.parent) === null || _a === void 0 ? void 0 : _a.lastChild) === null || _b === void 0 ? void 0 : _b.name) != "CloseTag" &&
(name = elementName(state$1.doc, tag.parent, head))) {
let to = head + (state$1.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
let insert = `</${name}>`;
return { range, changes: { from: head, to, insert } };
}
}
else if (didType && text == "/" && after.name == "StartCloseTag") {
let base = after.parent;
if (after.from == head - 2 && ((_c = base.lastChild) === null || _c === void 0 ? void 0 : _c.name) != "CloseTag" &&
(name = elementName(state$1.doc, base, head))) {
let to = head + (state$1.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
let insert = `${name}>`;
return {
range: state.EditorSelection.cursor(head + insert.length, -1),
changes: { from: head, to, insert }
};
}
}
return { range };
});
if (closeTags.changes.empty)
return false;
view.dispatch([
base,
state$1.update(closeTags, {
userEvent: "input.complete",
scrollIntoView: true
})
]);
return true;
});
exports.autoCloseTags = autoCloseTags;
exports.completeFromSchema = completeFromSchema;
exports.xml = xml;
exports.xmlLanguage = xmlLanguage;
+104
View File
@@ -0,0 +1,104 @@
import * as _codemirror_state from '@codemirror/state';
import { LRLanguage, LanguageSupport } from '@codemirror/language';
import { Completion, CompletionSource } from '@codemirror/autocomplete';
/**
Describes an element in your XML document schema.
*/
interface ElementSpec {
/**
The element name.
*/
name: string;
/**
Allowed children in this element. When not given, all elements
are allowed inside it.
*/
children?: readonly string[];
/**
When given, allows users to complete the given content strings
as plain text when at the start of the element.
*/
textContent?: readonly string[];
/**
Whether this element may appear at the top of the document.
*/
top?: boolean;
/**
Allowed attributes in this element. Strings refer to attributes
specified in [`XMLConfig.attrs`](https://codemirror.net/6/docs/ref/#lang-xml.XMLConfig.attrs), but
you can also provide one-off [attribute
specs](https://codemirror.net/6/docs/ref/#lang-xml.AttrSpec). Attributes marked as
[`global`](https://codemirror.net/6/docs/ref/#lang-xml.AttrSpec.global) are allowed in every
element, and don't have to be mentioned here.
*/
attributes?: readonly (string | AttrSpec)[];
/**
Can be provided to add extra fields to the
[completion](https://codemirror.net/6/docs/ref/#autocompletion.Completion) object created for this
element.
*/
completion?: Partial<Completion>;
}
/**
Describes an attribute in your XML schema.
*/
interface AttrSpec {
/**
The attribute name.
*/
name: string;
/**
Pre-defined values to complete for this attribute.
*/
values?: readonly (string | Completion)[];
/**
When `true`, this attribute can be added to all elements.
*/
global?: boolean;
/**
Provides extra fields to the
[completion](https://codemirror.net/6/docs/ref/#autocompletion.Completion) object created for this
element
*/
completion?: Partial<Completion>;
}
/**
Create a completion source for the given schema.
*/
declare function completeFromSchema(eltSpecs: readonly ElementSpec[], attrSpecs: readonly AttrSpec[]): CompletionSource;
/**
A language provider based on the [Lezer XML
parser](https://github.com/lezer-parser/xml), extended with
highlighting and indentation information.
*/
declare const xmlLanguage: LRLanguage;
type XMLConfig = {
/**
Provide a schema to create completions from.
*/
elements?: readonly ElementSpec[];
/**
Supporting attribute descriptions for the schema specified in
[`elements`](https://codemirror.net/6/docs/ref/#lang-xml.xml^conf.elements).
*/
attributes?: readonly AttrSpec[];
/**
Determines whether [`autoCloseTags`](https://codemirror.net/6/docs/ref/#lang-xml.autoCloseTags)
is included in the support extensions. Defaults to true.
*/
autoCloseTags?: boolean;
};
/**
XML language support. Includes schema-based autocompletion when
configured.
*/
declare function xml(conf?: XMLConfig): LanguageSupport;
/**
Extension that will automatically insert close tags when a `>` or
`/` is typed.
*/
declare const autoCloseTags: _codemirror_state.Extension;
export { type AttrSpec, type ElementSpec, autoCloseTags, completeFromSchema, xml, xmlLanguage };
+104
View File
@@ -0,0 +1,104 @@
import * as _codemirror_state from '@codemirror/state';
import { LRLanguage, LanguageSupport } from '@codemirror/language';
import { Completion, CompletionSource } from '@codemirror/autocomplete';
/**
Describes an element in your XML document schema.
*/
interface ElementSpec {
/**
The element name.
*/
name: string;
/**
Allowed children in this element. When not given, all elements
are allowed inside it.
*/
children?: readonly string[];
/**
When given, allows users to complete the given content strings
as plain text when at the start of the element.
*/
textContent?: readonly string[];
/**
Whether this element may appear at the top of the document.
*/
top?: boolean;
/**
Allowed attributes in this element. Strings refer to attributes
specified in [`XMLConfig.attrs`](https://codemirror.net/6/docs/ref/#lang-xml.XMLConfig.attrs), but
you can also provide one-off [attribute
specs](https://codemirror.net/6/docs/ref/#lang-xml.AttrSpec). Attributes marked as
[`global`](https://codemirror.net/6/docs/ref/#lang-xml.AttrSpec.global) are allowed in every
element, and don't have to be mentioned here.
*/
attributes?: readonly (string | AttrSpec)[];
/**
Can be provided to add extra fields to the
[completion](https://codemirror.net/6/docs/ref/#autocompletion.Completion) object created for this
element.
*/
completion?: Partial<Completion>;
}
/**
Describes an attribute in your XML schema.
*/
interface AttrSpec {
/**
The attribute name.
*/
name: string;
/**
Pre-defined values to complete for this attribute.
*/
values?: readonly (string | Completion)[];
/**
When `true`, this attribute can be added to all elements.
*/
global?: boolean;
/**
Provides extra fields to the
[completion](https://codemirror.net/6/docs/ref/#autocompletion.Completion) object created for this
element
*/
completion?: Partial<Completion>;
}
/**
Create a completion source for the given schema.
*/
declare function completeFromSchema(eltSpecs: readonly ElementSpec[], attrSpecs: readonly AttrSpec[]): CompletionSource;
/**
A language provider based on the [Lezer XML
parser](https://github.com/lezer-parser/xml), extended with
highlighting and indentation information.
*/
declare const xmlLanguage: LRLanguage;
type XMLConfig = {
/**
Provide a schema to create completions from.
*/
elements?: readonly ElementSpec[];
/**
Supporting attribute descriptions for the schema specified in
[`elements`](https://codemirror.net/6/docs/ref/#lang-xml.xml^conf.elements).
*/
attributes?: readonly AttrSpec[];
/**
Determines whether [`autoCloseTags`](https://codemirror.net/6/docs/ref/#lang-xml.autoCloseTags)
is included in the support extensions. Defaults to true.
*/
autoCloseTags?: boolean;
};
/**
XML language support. Includes schema-based autocompletion when
configured.
*/
declare function xml(conf?: XMLConfig): LanguageSupport;
/**
Extension that will automatically insert close tags when a `>` or
`/` is typed.
*/
declare const autoCloseTags: _codemirror_state.Extension;
export { type AttrSpec, type ElementSpec, autoCloseTags, completeFromSchema, xml, xmlLanguage };
+309
View File
@@ -0,0 +1,309 @@
import { parser } from '@lezer/xml';
import { syntaxTree, LRLanguage, indentNodeProp, foldNodeProp, bracketMatchingHandle, LanguageSupport } from '@codemirror/language';
import { EditorSelection } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
function tagName(doc, tag) {
let name = tag && tag.getChild("TagName");
return name ? doc.sliceString(name.from, name.to) : "";
}
function elementName$1(doc, tree) {
let tag = tree && tree.firstChild;
return !tag || tag.name != "OpenTag" ? "" : tagName(doc, tag);
}
function attrName(doc, tag, pos) {
let attr = tag && tag.getChildren("Attribute").find(a => a.from <= pos && a.to >= pos);
let name = attr && attr.getChild("AttributeName");
return name ? doc.sliceString(name.from, name.to) : "";
}
function findParentElement(tree) {
for (let cur = tree && tree.parent; cur; cur = cur.parent)
if (cur.name == "Element")
return cur;
return null;
}
function findLocation(state, pos) {
var _a;
let at = syntaxTree(state).resolveInner(pos, -1), inTag = null;
for (let cur = at; !inTag && cur.parent; cur = cur.parent)
if (cur.name == "OpenTag" || cur.name == "CloseTag" || cur.name == "SelfClosingTag" || cur.name == "MismatchedCloseTag")
inTag = cur;
if (inTag && (inTag.to > pos || inTag.lastChild.type.isError)) {
let elt = inTag.parent;
if (at.name == "TagName")
return inTag.name == "CloseTag" || inTag.name == "MismatchedCloseTag"
? { type: "closeTag", from: at.from, context: elt }
: { type: "openTag", from: at.from, context: findParentElement(elt) };
if (at.name == "AttributeName")
return { type: "attrName", from: at.from, context: inTag };
if (at.name == "AttributeValue")
return { type: "attrValue", from: at.from, context: inTag };
let before = at == inTag || at.name == "Attribute" ? at.childBefore(pos) : at;
if ((before === null || before === void 0 ? void 0 : before.name) == "StartTag")
return { type: "openTag", from: pos, context: findParentElement(elt) };
if ((before === null || before === void 0 ? void 0 : before.name) == "StartCloseTag" && before.to <= pos)
return { type: "closeTag", from: pos, context: elt };
if ((before === null || before === void 0 ? void 0 : before.name) == "Is")
return { type: "attrValue", from: pos, context: inTag };
if (before)
return { type: "attrName", from: pos, context: inTag };
return null;
}
else if (at.name == "StartCloseTag") {
return { type: "closeTag", from: pos, context: at.parent };
}
while (at.parent && at.to == pos && !((_a = at.lastChild) === null || _a === void 0 ? void 0 : _a.type.isError))
at = at.parent;
if (at.name == "Element" || at.name == "Text" || at.name == "Document")
return { type: "tag", from: pos, context: at.name == "Element" ? at : findParentElement(at) };
return null;
}
class Element {
constructor(spec, attrs, attrValues) {
this.attrs = attrs;
this.attrValues = attrValues;
this.children = [];
this.name = spec.name;
this.completion = Object.assign(Object.assign({ type: "type" }, spec.completion || {}), { label: this.name });
this.openCompletion = Object.assign(Object.assign({}, this.completion), { label: "<" + this.name });
this.closeCompletion = Object.assign(Object.assign({}, this.completion), { label: "</" + this.name + ">", boost: 2 });
this.closeNameCompletion = Object.assign(Object.assign({}, this.completion), { label: this.name + ">" });
this.text = spec.textContent ? spec.textContent.map(s => ({ label: s, type: "text" })) : [];
}
}
const Identifier = /^[:\-\.\w\u00b7-\uffff]*$/;
function attrCompletion(spec) {
return Object.assign(Object.assign({ type: "property" }, spec.completion || {}), { label: spec.name });
}
function valueCompletion(spec) {
return typeof spec == "string" ? { label: `"${spec}"`, type: "constant" }
: /^"/.test(spec.label) ? spec
: Object.assign(Object.assign({}, spec), { label: `"${spec.label}"` });
}
/**
Create a completion source for the given schema.
*/
function completeFromSchema(eltSpecs, attrSpecs) {
let allAttrs = [], globalAttrs = [];
let attrValues = Object.create(null);
for (let s of attrSpecs) {
let completion = attrCompletion(s);
allAttrs.push(completion);
if (s.global)
globalAttrs.push(completion);
if (s.values)
attrValues[s.name] = s.values.map(valueCompletion);
}
let allElements = [], topElements = [];
let byName = Object.create(null);
for (let s of eltSpecs) {
let attrs = globalAttrs, attrVals = attrValues;
if (s.attributes)
attrs = attrs.concat(s.attributes.map(s => {
if (typeof s == "string")
return allAttrs.find(a => a.label == s) || { label: s, type: "property" };
if (s.values) {
if (attrVals == attrValues)
attrVals = Object.create(attrVals);
attrVals[s.name] = s.values.map(valueCompletion);
}
return attrCompletion(s);
}));
let elt = new Element(s, attrs, attrVals);
byName[elt.name] = elt;
allElements.push(elt);
if (s.top)
topElements.push(elt);
}
if (!topElements.length)
topElements = allElements;
for (let i = 0; i < allElements.length; i++) {
let s = eltSpecs[i], elt = allElements[i];
if (s.children) {
for (let ch of s.children)
if (byName[ch])
elt.children.push(byName[ch]);
}
else {
elt.children = allElements;
}
}
return cx => {
var _a;
let { doc } = cx.state, loc = findLocation(cx.state, cx.pos);
if (!loc || (loc.type == "tag" && !cx.explicit))
return null;
let { type, from, context } = loc;
if (type == "openTag") {
let children = topElements;
let parentName = elementName$1(doc, context);
if (parentName) {
let parent = byName[parentName];
children = (parent === null || parent === void 0 ? void 0 : parent.children) || allElements;
}
return {
from,
options: children.map(ch => ch.completion),
validFor: Identifier
};
}
else if (type == "closeTag") {
let parentName = elementName$1(doc, context);
return parentName ? {
from,
to: cx.pos + (doc.sliceString(cx.pos, cx.pos + 1) == ">" ? 1 : 0),
options: [((_a = byName[parentName]) === null || _a === void 0 ? void 0 : _a.closeNameCompletion) || { label: parentName + ">", type: "type" }],
validFor: Identifier
} : null;
}
else if (type == "attrName") {
let parent = byName[tagName(doc, context)];
return {
from,
options: (parent === null || parent === void 0 ? void 0 : parent.attrs) || globalAttrs,
validFor: Identifier
};
}
else if (type == "attrValue") {
let attr = attrName(doc, context, from);
if (!attr)
return null;
let parent = byName[tagName(doc, context)];
let values = ((parent === null || parent === void 0 ? void 0 : parent.attrValues) || attrValues)[attr];
if (!values || !values.length)
return null;
return {
from,
to: cx.pos + (doc.sliceString(cx.pos, cx.pos + 1) == '"' ? 1 : 0),
options: values,
validFor: /^"[^"]*"?$/
};
}
else if (type == "tag") {
let parentName = elementName$1(doc, context), parent = byName[parentName];
let closing = [], last = context && context.lastChild;
if (parentName && (!last || last.name != "CloseTag" || tagName(doc, last) != parentName))
closing.push(parent ? parent.closeCompletion : { label: "</" + parentName + ">", type: "type", boost: 2 });
let options = closing.concat(((parent === null || parent === void 0 ? void 0 : parent.children) || (context ? allElements : topElements)).map(e => e.openCompletion));
if (context && (parent === null || parent === void 0 ? void 0 : parent.text.length)) {
let openTag = context.firstChild;
if (openTag.to > cx.pos - 20 && !/\S/.test(cx.state.sliceDoc(openTag.to, cx.pos)))
options = options.concat(parent.text);
}
return {
from,
options,
validFor: /^<\/?[:\-\.\w\u00b7-\uffff]*$/
};
}
else {
return null;
}
};
}
/**
A language provider based on the [Lezer XML
parser](https://github.com/lezer-parser/xml), extended with
highlighting and indentation information.
*/
const xmlLanguage = /*@__PURE__*/LRLanguage.define({
name: "xml",
parser: /*@__PURE__*/parser.configure({
props: [
/*@__PURE__*/indentNodeProp.add({
Element(context) {
let closed = /^\s*<\//.test(context.textAfter);
return context.lineIndent(context.node.from) + (closed ? 0 : context.unit);
},
"OpenTag CloseTag SelfClosingTag"(context) {
return context.column(context.node.from) + context.unit;
}
}),
/*@__PURE__*/foldNodeProp.add({
Element(subtree) {
let first = subtree.firstChild, last = subtree.lastChild;
if (!first || first.name != "OpenTag")
return null;
return { from: first.to, to: last.name == "CloseTag" ? last.from : subtree.to };
}
}),
/*@__PURE__*/bracketMatchingHandle.add({
"OpenTag CloseTag": node => node.getChild("TagName")
})
]
}),
languageData: {
commentTokens: { block: { open: "<!--", close: "-->" } },
indentOnInput: /^\s*<\/$/
}
});
/**
XML language support. Includes schema-based autocompletion when
configured.
*/
function xml(conf = {}) {
let support = [xmlLanguage.data.of({
autocomplete: completeFromSchema(conf.elements || [], conf.attributes || [])
})];
if (conf.autoCloseTags !== false)
support.push(autoCloseTags);
return new LanguageSupport(xmlLanguage, support);
}
function elementName(doc, tree, max = doc.length) {
if (!tree)
return "";
let tag = tree.firstChild;
let name = tag && tag.getChild("TagName");
return name ? doc.sliceString(name.from, Math.min(name.to, max)) : "";
}
/**
Extension that will automatically insert close tags when a `>` or
`/` is typed.
*/
const autoCloseTags = /*@__PURE__*/EditorView.inputHandler.of((view, from, to, text, insertTransaction) => {
if (view.composing || view.state.readOnly || from != to || (text != ">" && text != "/") ||
!xmlLanguage.isActiveAt(view.state, from, -1))
return false;
let base = insertTransaction(), { state } = base;
let closeTags = state.changeByRange(range => {
var _a, _b, _c;
let { head } = range;
let didType = state.doc.sliceString(head - 1, head) == text;
let after = syntaxTree(state).resolveInner(head, -1), name;
if (didType && text == ">" && after.name == "EndTag") {
let tag = after.parent;
if (((_b = (_a = tag.parent) === null || _a === void 0 ? void 0 : _a.lastChild) === null || _b === void 0 ? void 0 : _b.name) != "CloseTag" &&
(name = elementName(state.doc, tag.parent, head))) {
let to = head + (state.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
let insert = `</${name}>`;
return { range, changes: { from: head, to, insert } };
}
}
else if (didType && text == "/" && after.name == "StartCloseTag") {
let base = after.parent;
if (after.from == head - 2 && ((_c = base.lastChild) === null || _c === void 0 ? void 0 : _c.name) != "CloseTag" &&
(name = elementName(state.doc, base, head))) {
let to = head + (state.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
let insert = `${name}>`;
return {
range: EditorSelection.cursor(head + insert.length, -1),
changes: { from: head, to, insert }
};
}
}
return { range };
});
if (closeTags.changes.empty)
return false;
view.dispatch([
base,
state.update(closeTags, {
userEvent: "input.complete",
scrollIntoView: true
})
]);
return true;
});
export { autoCloseTags, completeFromSchema, xml, xmlLanguage };
+43
View File
@@ -0,0 +1,43 @@
{
"name": "@codemirror/lang-xml",
"version": "6.1.0",
"description": "XML language support for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",
"prepare": "cm-buildhelper src/xml.ts"
},
"keywords": [
"editor",
"code"
],
"author": {
"name": "Marijn Haverbeke",
"email": "marijn@haverbeke.berlin",
"url": "http://marijnhaverbeke.nl"
},
"type": "module",
"main": "dist/index.cjs",
"exports": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"types": "dist/index.d.ts",
"module": "dist/index.js",
"sideEffects": false,
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.4.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.0.0",
"@lezer/xml": "^1.0.0"
},
"devDependencies": {
"@codemirror/buildhelper": "^1.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/codemirror/lang-xml.git"
}
}