PluginProbe
Code Snippets / 3.2.1
Code Snippets v3.2.1
3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 3.0.1 All 64 releases
code-snippets / js / php-lint.ts

php-lint.ts in Code Snippets 3.2.1, at js/php-lint.ts

154 lines 3.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Based on work distributed under the BSD 3-Clause License (https://rawgit.com/glayzzle/codemirror-linter/master/LICENSE)
3 */
4
5 import Parser, { Block, Location, Node } from 'php-parser'
6 import * as CodeMirror from 'codemirror'
7
8 type Annotation = { message: string, severity: string, from: CodeMirror.Position, to: CodeMirror.Position }
9
10 interface Identifier extends Node {
11 name: string
12 }
13
14 interface Declaration extends Node {
15 name: Identifier | string
16 }
17
18 class Linter {
19 private readonly code: string
20
21 private readonly function_names: Set<string>
22
23 private readonly class_names: Set<string>
24
25 public readonly annotations: Annotation[]
26
27 /**
28 * Constructor.
29 * @param code
30 */
31 constructor(code: string) {
32 this.code = code
33 this.annotations = []
34
35 this.function_names = new Set()
36 this.class_names = new Set()
37 }
38
39 /**
40 * Lint the provided code.
41 */
42 lint() {
43 const parser = new Parser({
44 parser: {
45 suppressErrors: true,
46 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
47 // @ts-ignore types file has not been updated to support this
48 version: 800
49 },
50 ast: {
51 withPositions: true
52 }
53 })
54
55 try {
56 const ast = parser.parseEval(this.code)
57
58 // Process any errors caught by the parser.
59 if (ast.errors && 0 < ast.errors.length) {
60 for (const error of ast.errors) {
61 this.annotate(error.message as string, error.loc)
62 }
63 }
64
65 // Visit each node to perform additional checks.
66 this.visit(ast)
67
68 } catch (error) {
69 // eslint-disable-next-line no-console
70 console.error(error)
71 }
72 }
73
74 /**
75 * Visit nodes recursively.
76 * @param node
77 */
78 visit(node: Node) {
79
80 if (node.kind) {
81 this.validate(node)
82 }
83
84 if ('children' in node) {
85 const block = node as Block
86 for (const child of block.children) {
87 this.visit(child)
88 }
89 }
90 }
91
92 /**
93 * Check whether a given identifier has already been defined, creating an annotation if so.
94 * @param identifier
95 * @param registry
96 * @param label
97 */
98 checkDuplicateIdentifier(identifier: Identifier, registry: Set<string>, label: string) {
99 if (registry.has(identifier.name)) {
100 this.annotate(`Cannot redeclare ${label} ${identifier.name}()`, identifier.loc)
101 } else {
102 registry.add(identifier.name)
103 }
104 }
105
106 /**
107 * Perform additional validations on nodes.
108 * @param node
109 */
110 validate(node: Node) {
111 const decl = node as Declaration
112 const ident = decl.name as Identifier
113
114 if (!('name' in decl && 'name' in ident) || 'identifier' !== ident.kind) {
115 return
116 }
117
118 if ('function' === node.kind) {
119 this.checkDuplicateIdentifier(ident, this.function_names, 'function')
120
121 } else if ('class' === node.kind) {
122 this.checkDuplicateIdentifier(ident, this.class_names, 'class')
123 }
124 }
125
126 /**
127 * Create a lint annotation.
128 * @param message
129 * @param location
130 * @param severity
131 */
132 annotate(message: string, location: Location, severity = 'error') {
133 if (!location.start || !location.end) return
134
135 const [start, end] = location.end.offset < location.start.offset ?
136 [location.end, location.start] :
137 [location.start, location.end]
138
139 this.annotations.push({
140 message,
141 severity,
142 from: CodeMirror.Pos(start.line as number - 1, start.column as number),
143 to: CodeMirror.Pos(end.line as number - 1, end.column as number)
144 })
145 }
146 }
147
148 CodeMirror.registerHelper('lint', 'php', (text: string) => {
149 const linter = new Linter(text)
150 linter.lint()
151
152 return linter.annotations
153 })
154