pax_global_header00006660000000000000000000000064145651457040014525gustar00rootroot0000000000000052 comment=7edd3df9b0df41aef7c9835efac53fb52b747282 lang-javascript-6.2.2/000077500000000000000000000000001456514570400146215ustar00rootroot00000000000000lang-javascript-6.2.2/.github/000077500000000000000000000000001456514570400161615ustar00rootroot00000000000000lang-javascript-6.2.2/.github/workflows/000077500000000000000000000000001456514570400202165ustar00rootroot00000000000000lang-javascript-6.2.2/.github/workflows/dispatch.yml000066400000000000000000000006371456514570400225460ustar00rootroot00000000000000name: 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 lang-javascript-6.2.2/.gitignore000066400000000000000000000001271456514570400166110ustar00rootroot00000000000000/node_modules package-lock.json /dist /test/*.js /test/*.d.ts /test/*.d.ts.map .tern-* lang-javascript-6.2.2/.npmignore000066400000000000000000000001001456514570400166070ustar00rootroot00000000000000/src /test /node_modules .tern-* rollup.config.js tsconfig.json lang-javascript-6.2.2/CHANGELOG.md000066400000000000000000000071561456514570400164430ustar00rootroot00000000000000## 6.2.2 (2024-02-20) ### Bug fixes Fix a bug that would cause self-closing JSX tags to have another closing tag inserted when typing the final '>'. ## 6.2.1 (2023-08-28) ### Bug fixes `autoCloseTags` now generates two separate transactions, so that the completion can be undone separately. ## 6.2.0 (2023-08-26) ### New features Export a `typescriptSnippets` array and include TypeScript keyword completions in the default support extension when in TypeScript mode. ## 6.1.9 (2023-06-02) ### Bug fixes Make sure `scopeCompletionSource` doesn't try to complete property names that aren't simple identifier (such as numeric indices). ## 6.1.8 (2023-05-13) ### Bug fixes Stop completing keywords after `.` tokens. ## 6.1.7 (2023-04-19) ### Bug fixes Fix overeager JSX tag closing inside attribute values and in self-closing tags. ## 6.1.6 (2023-04-13) ### Bug fixes Fix a bug that allowed `autoCloseTags` to close JSX tags in JavaScript context. ## 6.1.5 (2023-04-04) ### Bug fixes Make TypeScript object type syntax foldable. ## 6.1.4 (2023-02-13) ### Bug fixes Make sure code in JSX context can be commented correctly. ## 6.1.3 (2023-02-02) ### Bug fixes Fix auto-closing of JSX fragments. ## 6.1.2 (2022-12-07) ### Bug fixes Automatic tag closing in JSX now works for namespaced and member-expression tag names. ## 6.1.1 (2022-10-24) ### Bug fixes Make `completionPath` handle `?.` syntax. ## 6.1.0 (2022-09-20) ### New features The `completionPath` helper can now be used to find the object path to complete at a given position. `scopeCompletionSource` provides a completion source based on a scope object. ## 6.0.2 (2022-07-21) ### Bug fixes Fix the `source` field in ESLint diagnostics to properly hold `"eslint"`. Fix (non-)auto indentation in template strings and comments. ## 6.0.1 (2022-06-29) ### Bug fixes Avoid completing variables/keywords in property or definition positions. Fix a bug that broke local variable completion if JavaScript was parsed an overlay in an outer language. ## 6.0.0 (2022-06-08) ### Breaking changes Update dependencies to 6.0.0 ## 0.20.1 (2022-06-01) ### New features `localCompletionSource` (included in the support extensions returned from `javascript`) now provides a way to complete locally-defined names. ## 0.20.0 (2022-04-20) ### New features The new `autoCloseTags` extension (included by default in the `javascript` language extension when `jsx` is configured) finishes JSX closing tags when you type a `>` or `/` character. ## 0.19.7 (2022-01-28) ## 0.19.6 (2022-01-11) ### Bug fixes Remove accidentally released unfinished changes. ## 0.19.5 (2022-01-11) ### Bug fixes Add the `function` highlight modifier to variables used in tagged template expressions. ## 0.19.4 (2022-01-03) ### Bug fixes Fix highlighting of TypeScript private/public/protected keywords. ## 0.19.3 (2021-11-12) ### Bug fixes Add styling for private properties. ## 0.19.2 (2021-09-23) ### New features Use more specific highlighting tags for JSX 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) ### Bug fixes Extend `indentOnInput` expression to cover closing JSX tags. ## 0.17.2 (2021-02-15) ### Bug fixes Improve highlighting tag specificity of defined function and class names. Add indentation information for JSX constructs Support smart indent for JSX syntax. ## 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. lang-javascript-6.2.2/LICENSE000066400000000000000000000021361456514570400156300ustar00rootroot00000000000000MIT License Copyright (C) 2018-2021 by Marijn Haverbeke 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. lang-javascript-6.2.2/README.md000066400000000000000000000176031456514570400161070ustar00rootroot00000000000000 # @codemirror/lang-javascript [![NPM version](https://img.shields.io/npm/v/@codemirror/lang-javascript.svg)](https://www.npmjs.org/package/@codemirror/lang-javascript) [ [**WEBSITE**](https://codemirror.net/) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/lang-javascript/blob/main/CHANGELOG.md) ] This package implements JavaScript 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-javascript/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
javascript(config⁠?: {jsx⁠?: boolean, typescript⁠?: boolean} = {}) → LanguageSupport

JavaScript support. Includes snippet and local variable completion.

javascriptLanguage: LRLanguage

A language provider based on the Lezer JavaScript parser, extended with highlighting and indentation information.

typescriptLanguage: LRLanguage

A language provider for TypeScript.

jsxLanguage: LRLanguage

Language provider for JSX.

tsxLanguage: LRLanguage

Language provider for JSX + TypeScript.

autoCloseTags: Extension

Extension that will automatically insert JSX close tags when a > or / is typed.

snippets: readonly Completion[]

A collection of JavaScript-related snippets.

typescriptSnippets: Completion[]

A collection of snippet completions for TypeScript. Includes the JavaScript snippets.

localCompletionSource(contextCompletionContext) → CompletionResult | null

Completion source that looks up locally defined names in JavaScript code.

completionPath(contextCompletionContext) → {path: readonly string[], name: string} | null

Helper function for defining JavaScript completion sources. It returns the completable name and object path for a completion context, or null if no name/property completion should happen at that position. For example, when completing after a.b.c it will return {path: ["a", "b"], name: "c"}. When completing after x it will return {path: [], name: "x"}. When not in a property or name, it will return null if context.explicit is false, and {path: [], name: ""} otherwise.

scopeCompletionSource(scope: any) → CompletionSource

Defines a completion source that completes from the given scope object (for example globalThis). Will enter properties of the object when completing properties on a directly-named path.

esLint(eslint: any, config⁠?: any) → fn(viewEditorView) → Diagnostic[]

Connects an ESLint linter to CodeMirror's lint integration. eslint should be an instance of the Linter class, and config an optional ESLint configuration. The return value of this function can be passed to linter to create a JavaScript linting extension.

Note that ESLint targets node, and is tricky to run in the browser. The eslint-linter-browserify package may help with that (see example).

lang-javascript-6.2.2/package.json000066400000000000000000000021451456514570400171110ustar00rootroot00000000000000{ "name": "@codemirror/lang-javascript", "version": "6.2.2", "description": "JavaScript language support for the CodeMirror code editor", "scripts": { "test": "cm-runtests", "prepare": "cm-buildhelper src/index.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.6.0", "@codemirror/lint": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/javascript": "^1.0.0" }, "devDependencies": { "@codemirror/buildhelper": "^1.0.0", "@lezer/lr": "^1.0.0" }, "repository": { "type": "git", "url": "https://github.com/codemirror/lang-javascript.git" } } lang-javascript-6.2.2/src/000077500000000000000000000000001456514570400154105ustar00rootroot00000000000000lang-javascript-6.2.2/src/README.md000066400000000000000000000024051456514570400166700ustar00rootroot00000000000000 # @codemirror/lang-javascript [![NPM version](https://img.shields.io/npm/v/@codemirror/lang-javascript.svg)](https://www.npmjs.org/package/@codemirror/lang-javascript) [ [**WEBSITE**](https://codemirror.net/) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/lang-javascript/blob/main/CHANGELOG.md) ] This package implements JavaScript 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-javascript/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 @javascript @javascriptLanguage @typescriptLanguage @jsxLanguage @tsxLanguage @autoCloseTags @snippets @typescriptSnippets @localCompletionSource @completionPath @scopeCompletionSource @esLint lang-javascript-6.2.2/src/complete.ts000066400000000000000000000150461456514570400175760ustar00rootroot00000000000000import {NodeWeakMap, SyntaxNodeRef, SyntaxNode, IterMode} from "@lezer/common" import {Completion, CompletionContext, CompletionResult, CompletionSource} from "@codemirror/autocomplete" import {syntaxTree} from "@codemirror/language" import {Text} from "@codemirror/state" const cache = new NodeWeakMap() const ScopeNodes = new Set([ "Script", "Block", "FunctionExpression", "FunctionDeclaration", "ArrowFunction", "MethodDeclaration", "ForStatement" ]) function defID(type: string) { return (node: SyntaxNodeRef, def: (node: SyntaxNodeRef, type: string) => void) => { let id = node.node.getChild("VariableDefinition") if (id) def(id, type) return true } } const functionContext = ["FunctionDeclaration"] const gatherCompletions: { [node: string]: (node: SyntaxNodeRef, def: (node: SyntaxNodeRef, type: string) => void) => void | boolean } = { FunctionDeclaration: defID("function"), ClassDeclaration: defID("class"), ClassExpression: () => true, EnumDeclaration: defID("constant"), TypeAliasDeclaration: defID("type"), NamespaceDeclaration: defID("namespace"), VariableDefinition(node, def) { if (!node.matchContext(functionContext)) def(node, "variable") }, TypeDefinition(node, def) { def(node, "type") }, __proto__: null as any } function getScope(doc: Text, node: SyntaxNode) { let cached = cache.get(node) if (cached) return cached let completions: Completion[] = [], top = true function def(node: SyntaxNodeRef, type: string) { let name = doc.sliceString(node.from, node.to) completions.push({label: name, type}) } node.cursor(IterMode.IncludeAnonymous).iterate(node => { if (top) { top = false } else if (node.name) { let gather = gatherCompletions[node.name] if (gather && gather(node, def) || ScopeNodes.has(node.name)) return false } else if (node.to - node.from > 8192) { // Allow caching for bigger internal nodes for (let c of getScope(doc, node.node)) completions.push(c) return false } }) cache.set(node, completions) return completions } const Identifier = /^[\w$\xa1-\uffff][\w$\d\xa1-\uffff]*$/ export const dontComplete = [ "TemplateString", "String", "RegExp", "LineComment", "BlockComment", "VariableDefinition", "TypeDefinition", "Label", "PropertyDefinition", "PropertyName", "PrivatePropertyDefinition", "PrivatePropertyName", ".", "?." ] /// Completion source that looks up locally defined names in /// JavaScript code. export function localCompletionSource(context: CompletionContext): CompletionResult | null { let inner = syntaxTree(context.state).resolveInner(context.pos, -1) if (dontComplete.indexOf(inner.name) > -1) return null let isWord = inner.name == "VariableName" || inner.to - inner.from < 20 && Identifier.test(context.state.sliceDoc(inner.from, inner.to)) if (!isWord && !context.explicit) return null let options: Completion[] = [] for (let pos: SyntaxNode | null = inner; pos; pos = pos.parent) { if (ScopeNodes.has(pos.name)) options = options.concat(getScope(context.state.doc, pos)) } return { options, from: isWord ? inner.from : context.pos, validFor: Identifier } } function pathFor(read: (node: SyntaxNode) => string, member: SyntaxNode, name: string) { let path: string[] = [] for (;;) { let obj = member.firstChild, prop if (obj?.name == "VariableName") { path.push(read(obj)) return {path: path.reverse(), name} } else if (obj?.name == "MemberExpression" && (prop = obj.lastChild)?.name == "PropertyName") { path.push(read(prop!)) member = obj } else { return null } } } /// Helper function for defining JavaScript completion sources. It /// returns the completable name and object path for a completion /// context, or null if no name/property completion should happen at /// that position. For example, when completing after `a.b.c` it will /// return `{path: ["a", "b"], name: "c"}`. When completing after `x` /// it will return `{path: [], name: "x"}`. When not in a property or /// name, it will return null if `context.explicit` is false, and /// `{path: [], name: ""}` otherwise. export function completionPath(context: CompletionContext): {path: readonly string[], name: string} | null { let read = (node: SyntaxNode) => context.state.doc.sliceString(node.from, node.to) let inner = syntaxTree(context.state).resolveInner(context.pos, -1) if (inner.name == "PropertyName") { return pathFor(read, inner.parent!, read(inner)) } else if ((inner.name == "." || inner.name == "?.") && inner.parent!.name == "MemberExpression") { return pathFor(read, inner.parent!, "") } else if (dontComplete.indexOf(inner.name) > -1) { return null } else if (inner.name == "VariableName" || inner.to - inner.from < 20 && Identifier.test(read(inner))) { return {path: [], name: read(inner)} } else if (inner.name == "MemberExpression") { return pathFor(read, inner, "") } else { return context.explicit ? {path: [], name: ""} : null } } function enumeratePropertyCompletions(obj: any, top: boolean): readonly Completion[] { let options: Completion[] = [], seen: Set = new Set for (let depth = 0;; depth++) { for (let name of (Object.getOwnPropertyNames || Object.keys)(obj)) { if (!/^[a-zA-Z_$\xaa-\uffdc][\w$\xaa-\uffdc]*$/.test(name) || seen.has(name)) continue seen.add(name) let value try { value = obj[name] } catch(_) { continue } options.push({ label: name, type: typeof value == "function" ? (/^[A-Z]/.test(name) ? "class" : top ? "function" : "method") : top ? "variable" : "property", boost: -depth }) } let next = Object.getPrototypeOf(obj) if (!next) return options obj = next } } /// Defines a [completion source](#autocomplete.CompletionSource) that /// completes from the given scope object (for example `globalThis`). /// Will enter properties of the object when completing properties on /// a directly-named path. export function scopeCompletionSource(scope: any): CompletionSource { let cache: Map = new Map return (context: CompletionContext) => { let path = completionPath(context) if (!path) return null let target = scope for (let step of path.path) { target = target[step] if (!target) return null } let options = cache.get(target) if (!options) cache.set(target, options = enumeratePropertyCompletions(target, !path.path.length)) return { from: context.pos - path.name.length, options, validFor: Identifier } } } lang-javascript-6.2.2/src/eslint.ts000066400000000000000000000052761456514570400172700ustar00rootroot00000000000000import {Diagnostic} from "@codemirror/lint" import {Text} from "@codemirror/state" import {EditorView} from "@codemirror/view" import {javascriptLanguage} from "./javascript" /// Connects an [ESLint](https://eslint.org/) linter to CodeMirror's /// [lint](#lint) integration. `eslint` should be an instance of the /// [`Linter`](https://eslint.org/docs/developer-guide/nodejs-api#linter) /// class, and `config` an optional ESLint configuration. The return /// value of this function can be passed to [`linter`](#lint.linter) /// to create a JavaScript linting extension. /// /// Note that ESLint targets node, and is tricky to run in the /// browser. The /// [eslint-linter-browserify](https://github.com/UziTech/eslint-linter-browserify) /// package may help with that (see /// [example](https://github.com/UziTech/eslint-linter-browserify/blob/master/example/script.js)). export function esLint(eslint: any, config?: any) { if (!config) { config = { parserOptions: {ecmaVersion: 2019, sourceType: "module"}, env: {browser: true, node: true, es6: true, es2015: true, es2017: true, es2020: true}, rules: {} } eslint.getRules().forEach((desc: any, name: string) => { if (desc.meta.docs.recommended) config.rules[name] = 2 }) } return (view: EditorView) => { let {state} = view, found: Diagnostic[] = [] for (let {from, to} of javascriptLanguage.findRegions(state)) { let fromLine = state.doc.lineAt(from), offset = {line: fromLine.number - 1, col: from - fromLine.from, pos: from} for (let d of eslint.verify(state.sliceDoc(from, to), config)) found.push(translateDiagnostic(d, state.doc, offset)) } return found } } function mapPos(line: number, col: number, doc: Text, offset: {line: number, col: number, pos: number}) { return doc.line(line + offset.line).from + col + (line == 1 ? offset.col - 1 : -1) } function translateDiagnostic(input: any, doc: Text, offset: {line: number, col: number, pos: number}): Diagnostic { let start = mapPos(input.line, input.column, doc, offset) let result: Diagnostic = { from: start, to: input.endLine != null && input.endColumn != 1 ? mapPos(input.endLine, input.endColumn, doc, offset) : start, message: input.message, source: input.ruleId ? "eslint:" + input.ruleId : "eslint", severity: input.severity == 1 ? "warning" : "error", } if (input.fix) { let {range, text} = input.fix, from = range[0] + offset.pos - start, to = range[1] + offset.pos - start result.actions = [{ name: "fix", apply(view: EditorView, start: number) { view.dispatch({changes: {from: start + from, to: start + to, insert: text}, scrollIntoView: true}) } }] } return result } lang-javascript-6.2.2/src/index.ts000066400000000000000000000004511456514570400170670ustar00rootroot00000000000000export {javascriptLanguage, typescriptLanguage, jsxLanguage, tsxLanguage, autoCloseTags, javascript} from "./javascript" export {snippets, typescriptSnippets} from "./snippets" export {esLint} from "./eslint" export {localCompletionSource, completionPath, scopeCompletionSource} from "./complete" lang-javascript-6.2.2/src/javascript.ts000066400000000000000000000154721456514570400201370ustar00rootroot00000000000000import {parser} from "@lezer/javascript" import {SyntaxNode} from "@lezer/common" import {LRLanguage, LanguageSupport, Sublanguage, sublanguageProp, defineLanguageFacet, delimitedIndent, flatIndent, continuedIndent, indentNodeProp, foldNodeProp, foldInside, syntaxTree} from "@codemirror/language" import {EditorSelection, Text} from "@codemirror/state" import {EditorView} from "@codemirror/view" import {completeFromList, ifNotIn} from "@codemirror/autocomplete" import {snippets, typescriptSnippets} from "./snippets" import {localCompletionSource, dontComplete} from "./complete" /// A language provider based on the [Lezer JavaScript /// parser](https://github.com/lezer-parser/javascript), extended with /// highlighting and indentation information. export const javascriptLanguage = LRLanguage.define({ name: "javascript", parser: parser.configure({ props: [ indentNodeProp.add({ IfStatement: continuedIndent({except: /^\s*({|else\b)/}), TryStatement: continuedIndent({except: /^\s*({|catch\b|finally\b)/}), LabeledStatement: flatIndent, SwitchBody: context => { let after = context.textAfter, closed = /^\s*\}/.test(after), isCase = /^\s*(case|default)\b/.test(after) return context.baseIndent + (closed ? 0 : isCase ? 1 : 2) * context.unit }, Block: delimitedIndent({closing: "}"}), ArrowFunction: cx => cx.baseIndent + cx.unit, "TemplateString BlockComment": () => null, "Statement Property": continuedIndent({except: /^{/}), JSXElement(context) { let closed = /^\s*<\//.test(context.textAfter) return context.lineIndent(context.node.from) + (closed ? 0 : context.unit) }, JSXEscape(context) { let closed = /\s*\}/.test(context.textAfter) return context.lineIndent(context.node.from) + (closed ? 0 : context.unit) }, "JSXOpenTag JSXSelfClosingTag"(context) { return context.column(context.node.from) + context.unit } }), foldNodeProp.add({ "Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType": foldInside, BlockComment(tree) { return {from: tree.from + 2, to: tree.to - 2} } }) ] }), languageData: { closeBrackets: {brackets: ["(", "[", "{", "'", '"', "`"]}, commentTokens: {line: "//", block: {open: "/*", close: "*/"}}, indentOnInput: /^\s*(?:case |default:|\{|\}|<\/)$/, wordChars: "$" } }) const jsxSublanguage: Sublanguage = { test: node => /^JSX/.test(node.name), facet: defineLanguageFacet({commentTokens: {block: {open: "{/*", close: "*/}"}}}) } /// A language provider for TypeScript. export const typescriptLanguage = javascriptLanguage.configure({dialect: "ts"}, "typescript") /// Language provider for JSX. export const jsxLanguage = javascriptLanguage.configure({ dialect: "jsx", props: [sublanguageProp.add(n => n.isTop ? [jsxSublanguage] : undefined)] }) /// Language provider for JSX + TypeScript. export const tsxLanguage = javascriptLanguage.configure({ dialect: "jsx ts", props: [sublanguageProp.add(n => n.isTop ? [jsxSublanguage] : undefined)] }, "typescript") let kwCompletion = (name: string) => ({label: name, type: "keyword"}) const keywords = "break case const continue default delete export extends false finally in instanceof let new return static super switch this throw true typeof var yield".split(" ").map(kwCompletion) const typescriptKeywords = keywords.concat(["declare", "implements", "private", "protected", "public"].map(kwCompletion)) /// JavaScript support. Includes [snippet](#lang-javascript.snippets) /// and local variable completion. export function javascript(config: {jsx?: boolean, typescript?: boolean} = {}) { let lang = config.jsx ? (config.typescript ? tsxLanguage : jsxLanguage) : config.typescript ? typescriptLanguage : javascriptLanguage let completions = config.typescript ? typescriptSnippets.concat(typescriptKeywords) : snippets.concat(keywords) return new LanguageSupport(lang, [ javascriptLanguage.data.of({ autocomplete: ifNotIn(dontComplete, completeFromList(completions)) }), javascriptLanguage.data.of({ autocomplete: localCompletionSource }), config.jsx ? autoCloseTags : [], ]) } function findOpenTag(node: SyntaxNode) { for (;;) { if (node.name == "JSXOpenTag" || node.name == "JSXSelfClosingTag" || node.name == "JSXFragmentTag") return node if (node.name == "JSXEscape" || !node.parent) return null node = node.parent } } function elementName(doc: Text, tree: SyntaxNode | null | undefined, max = doc.length) { for (let ch = tree?.firstChild; ch; ch = ch.nextSibling) { if (ch.name == "JSXIdentifier" || ch.name == "JSXBuiltin" || ch.name == "JSXNamespacedName" || ch.name == "JSXMemberExpression") return doc.sliceString(ch.from, Math.min(ch.to, max)) } return "" } const android = typeof navigator == "object" && /Android\b/.test(navigator.userAgent) /// Extension that will automatically insert JSX close tags when a `>` or /// `/` is typed. export const autoCloseTags = EditorView.inputHandler.of((view, from, to, text, defaultInsert) => { if ((android ? view.composing : view.compositionStarted) || view.state.readOnly || from != to || (text != ">" && text != "/") || !javascriptLanguage.isActiveAt(view.state, from, -1)) return false let base = defaultInsert(), {state} = base let closeTags = state.changeByRange(range => { let {head} = range, around = syntaxTree(state).resolveInner(head - 1, -1), name if (around.name == "JSXStartTag") around = around.parent! if (state.doc.sliceString(head - 1, head) != text || around.name == "JSXAttributeValue" && around.to > head) { // Ignore input inside attribute or cases where the text wasn't actually inserted } else if (text == ">" && around.name == "JSXFragmentTag") { return {range, changes: {from: head, insert: ``}} } else if (text == "/" && around.name == "JSXStartCloseTag") { let empty = around.parent!, base = empty.parent if (base && empty.from == head - 2 && ((name = elementName(state.doc, base.firstChild, head)) || base.firstChild?.name == "JSXFragmentTag")) { let insert = `${name}>` return {range: EditorSelection.cursor(head + insert.length, -1), changes: {from: head, insert}} } } else if (text == ">") { let openTag = findOpenTag(around) if (openTag && openTag.name == "JSXOpenTag" && !/^\/?>|^<\//.test(state.doc.sliceString(head, head + 2)) && (name = elementName(state.doc, openTag, head))) return {range, changes: {from: head, insert: ``}} } return {range} }) if (closeTags.changes.empty) return false view.dispatch([ base, state.update(closeTags, {userEvent: "input.complete", scrollIntoView: true}) ]) return true }); lang-javascript-6.2.2/src/snippets.ts000066400000000000000000000040071456514570400176260ustar00rootroot00000000000000import {Completion, snippetCompletion as snip} from "@codemirror/autocomplete" /// A collection of JavaScript-related /// [snippets](#autocomplete.snippet). export const snippets: readonly Completion[] = [ snip("function ${name}(${params}) {\n\t${}\n}", { label: "function", detail: "definition", type: "keyword" }), snip("for (let ${index} = 0; ${index} < ${bound}; ${index}++) {\n\t${}\n}", { label: "for", detail: "loop", type: "keyword" }), snip("for (let ${name} of ${collection}) {\n\t${}\n}", { label: "for", detail: "of loop", type: "keyword" }), snip("do {\n\t${}\n} while (${})", { label: "do", detail: "loop", type: "keyword" }), snip("while (${}) {\n\t${}\n}", { label: "while", detail: "loop", type: "keyword" }), snip("try {\n\t${}\n} catch (${error}) {\n\t${}\n}", { label: "try", detail: "/ catch block", type: "keyword" }), snip("if (${}) {\n\t${}\n}", { label: "if", detail: "block", type: "keyword" }), snip("if (${}) {\n\t${}\n} else {\n\t${}\n}", { label: "if", detail: "/ else block", type: "keyword" }), snip("class ${name} {\n\tconstructor(${params}) {\n\t\t${}\n\t}\n}", { label: "class", detail: "definition", type: "keyword" }), snip("import {${names}} from \"${module}\"\n${}", { label: "import", detail: "named", type: "keyword" }), snip("import ${name} from \"${module}\"\n${}", { label: "import", detail: "default", type: "keyword" }) ] /// A collection of snippet completions for TypeScript. Includes the /// JavaScript [snippets](#lang-javascript.snippets). export const typescriptSnippets = snippets.concat([ snip("interface ${name} {\n\t${}\n}", { label: "interface", detail: "definition", type: "keyword" }), snip("type ${name} = ${type}", { label: "type", detail: "definition", type: "keyword" }), snip("enum ${name} {\n\t${}\n}", { label: "enum", detail: "definition", type: "keyword" }) ]) lang-javascript-6.2.2/test/000077500000000000000000000000001456514570400156005ustar00rootroot00000000000000lang-javascript-6.2.2/test/test-indent.ts000066400000000000000000000047541456514570400204200ustar00rootroot00000000000000import ist from "ist" import {EditorState} from "@codemirror/state" import {getIndentation} from "@codemirror/language" import {javascript} from "@codemirror/lang-javascript" function check(code: string, options: any = {}) { return () => { code = /^\n*([^]*)/.exec(code)![1] let state = EditorState.create({doc: code, extensions: [javascript(options).language]}) for (let pos = 0, lines = code.split("\n"), i = 0; i < lines.length; i++) { let line = lines[i], indent = /^\s*/.exec(line)![0].length ist(`${getIndentation(state, pos)} (${i + 1})`, `${indent} (${i + 1})`) pos += line.length + 1 } } } describe("javascript indentation", () => { it("indents argument blocks", check(` foo({ bar, baz }) `)) it("indents function args", check(` foo( bar )`)) it("indents nested calls", check(` one( two( three( four() ) ) )`)) it("aligns lists", check(` one(two, three({four: five, six: seven }))`)) it("indents unfinished nodes", check(` if (foo && `)) it("deindents else", check(` if (1) a else b `)) it("support multiple opening calls on a line", check(` foo(bar(baz( ugh(quux( blah))))) `)) it("supports opening brackets on their own line", check(` const something = [ a, b ] `)) it("handles hanging braces", check(` function foo() { body() } `)) it("indents case bodies", check(` switch (1) { case 22: console.log(2) break default: return 2 }`)) it("indents method chains", check(` return blah .something() .anotherOne( withArguments ) .catch(x) `)) it("indents JSON-style", check(` let j = { foo: [ { 1: true, 2: false }, {}, ], quux: null } `)) it("indents continued properties", check(` let o = { foo: 1 + 3 + 4, bar: 11 } `)) it("doesn't get confused by continued param lists", check(` function foo(a, b, c, d) { return } `)) it("doesn't indent below labels", check(` abc: foo() `)) it("properly indents function expression arguments", check(` foo(100, function() { return 2 }) `)) it("indents arrow functions", check(` let x = a => { return 4 } let y = a => 6 let x = (a, b) => 6 `)) it("indents braceless structure", check(` for (;;) if (0) if (1) foo() else bar() else baz() `)) it("indents JSX constructs", check(` let y =
What?
`, {jsx: true})) }) lang-javascript-6.2.2/test/test-syntax.ts000066400000000000000000000025461456514570400204620ustar00rootroot00000000000000import ist from "ist" import {EditorState} from "@codemirror/state" import {javascriptLanguage} from "@codemirror/lang-javascript" import {ensureSyntaxTree} from "@codemirror/language" import {Tree} from "@lezer/common" function s(doc: string) { return EditorState.create({doc, extensions: [javascriptLanguage.extension]}) } function tr(state: EditorState) { return ensureSyntaxTree(state, state.doc.length, 1e9)! } describe("javascript syntax queries", () => { it("returns a tree", () => { let state = s("let state = s()"), tree = tr(state) ist(tree instanceof Tree) ist(tree.type.name, "Script") ist(tree.length, state.doc.length) let def = tree.resolve(6) ist(def.name, "VariableDefinition") ist(def.from, 4) ist(def.to, 9) }) it("keeps the tree up to date through changes", () => { let state = s("if (2)\n x") ist(tr(state).topNode.childAfter(0)!.name, "IfStatement") state = state.update({changes: {from: 0, to: 3, insert: "fac"}}).state ist(tr(state).topNode.childAfter(0)!.name, "ExpressionStatement") }) it("reuses nodes when parsing big documents", () => { let state = s("'hello';\n" + "blah;\n".repeat(3000)) let buf = (tr(state).resolve(2) as any).buffer state = state.update({changes: {from: 2000, to: 2020, insert: "xyz"}}).state ist((tr(state).resolve(2) as any).buffer, buf) }) })