-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathsemantic-php.el
278 lines (231 loc) · 10.2 KB
/
semantic-php.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
;;; semantic-php.el --- Semantic integration for PHP -*- lexical-binding: t; -*-
;; Copyright (C) 2014 Joris Steyn
;; Author: Joris Steyn <[email protected]>
;; Created: 1 Nov 2014
;; Keywords: languages
;; Homepage: https://github.com/jorissteyn/semantic-php
;; Version: 0
;; This file is not part of GNU Emacs.
;; This file is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 3
;; of the License, or (at your option) any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this file; if not, write to the Free Software
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
;; 02110-1301, USA.
;;; Commentary:
;; Installation from VCS for development purposes:
;;
;; 1. Clone the semantic-php repository
;; 2. Run 'make dist' to generate the parser and autoload definitions
;; 3. Load the generated semantic-php/loaddefs.el in your init file:
;;
;; (add-to-list 'load-path "~/path/to/semantic-php")
;; (load "~/path/to/semantic-php/loaddefs.el")
;;; Code:
(require 'semantic-php-wy)
(require 'semantic)
(require 'semantic/doc)
(require 'subr-x)
;;;###autoload
(add-hook 'php-mode-hook 'semantic-php-default-setup)
;;;###autoload
(defun semantic-php-default-setup ()
"Setup semantic-php in the current buffer"
(semantic-php-wy--install-parser)
(setq
;; Lexical analysis
semantic-lex-analyzer 'semantic-php-wy-lexer
;; Syntax table modifications
semantic-lex-syntax-modifications
'(
(?= ".")
(?& ".")
(?+ ".")
(?- ".")
(?| ".")
(?< ".")
(?> ".")
(?% ".")
(?' "\"")
(?\" "\"")
(?` "\"")
(?_ "w")
(?$ "_")
(?/ ". 124b")
(?* ". 23")
(?\n "> b")
(?# "< b"))
;; Semantic requires this expression for line-comments,
;; if lexing without major mode
semantic-lex-comment-regex "\\s<\\|\\(/\\*\\|//\\)"
;; Not related to semantic, this variable needs to be turned on in
;; order for comments to not get non-comment syntax properties in
;; their contents. This is also done by cc-mode (and thus
;; php-mode), and is only here to ensure correct parsing without a
;; major mode (test cases, batch project scan)
parse-sexp-ignore-comments t
;; Separators to use when finding context prefix
semantic-type-relation-separator-character '("::" "->")
semantic-command-separation-character ";"
;; Tag expansion
semantic-tag-expand-function 'semantic-php-expand-tag
;; imenu setup
semantic-imenu-summary-function 'semantic-format-tag-prototype
imenu-create-index-function 'semantic-create-imenu-index
;; Specify the labels of different tag types
semantic-bucketize-tag-class 'semantic-php-bucketize-tag-class
semantic-symbol->name-assoc-list '((namespace . "Namespaces")
(class . "Classes")
(interface . "Interfaces")
(trait . "Traits")
(variable . "Variables")
(constant . "Constants")
(function . "Functions"))
semantic-symbol->name-assoc-list-for-type-parts '((constant . "Constants")
(variable . "Properties")
(function . "Methods"))))
;; Define all modes applicable for semantic-php.
(define-child-mode web-mode php-mode)
(defun semantic-php-expand-tag (tag)
"Expand compound declarations found in TAG into separate tags.
If the name of the tag is a cons cell, assume (name . (start . end))
and set bounds accordingly."
;; TODO: Improve the incremental reparse of compound statements like
;; 'public $a, $b;'.
nil)
(defun semantic-php-bucketize-tag-class (tag)
"Get the type of given TAG.
This function augments the standard bucketize behaviour by
emitting separate symbols for classes, interfaces and traits."
(let ((tag-class (semantic-tag-class tag))
(tag-attribs (semantic-tag-attributes tag)))
;; For type tags, return the type attribute as set by the parser
;; (class, interface, trait). For all other tags, return the
;; regular tag class.
(if (eq tag-class 'type)
;; Convert string to symbol accepted by semantic-symbol->*
(intern (plist-get tag-attribs :type))
tag-class)))
(define-mode-local-override semantic-tag-components
php-mode (tag)
"Return a list of components for TAG."
(cond ((semantic-tag-of-class-p tag 'type)
(semantic-tag-type-members tag))
((semantic-tag-of-class-p tag 'function)
(append (semantic-tag-function-arguments tag)
(semantic-tag-type-members tag)))
(t nil)))
(define-mode-local-override semantic-get-local-variables
php-mode (&optional point)
"Get local values from the context of point.
This implementation is very different from
`semantic-get-local-variables-default'. We use the current parser
result to extract variable tags based on the current context, or
the top-level context when not in a function context. It will not
re-parse part of the buffer."
(let ((functiontag (semantic-current-tag-of-class 'function))
(functionparent (car-safe (semantic-find-tags-by-type
"class" (semantic-find-tag-by-overlay))))
classparen ;; the parent class of the function parent
alltags ;; collect all tags in scope (TODO: use scope object)
variabletags ;; list of variable tags
namelist) ;; the names of tags in variabletags (used for dedupping)
(setq alltags
(if functiontag
;; In a function.
(append (semantic-tag-function-arguments functiontag)
(semantic-tag-type-members functiontag))
;; In the toplevel space.
(semantic-fetch-tags)))
;; Find local variables and remove duplicates.
(dolist (tag alltags variabletags)
(when (and (semantic-tag-of-class-p tag 'variable)
(not (member (semantic-tag-name tag) namelist)))
(push (semantic-tag-name tag) namelist)
(push tag variabletags)))
;; Handle special function variables/keywords.
(when functionparent
;; If in non-static context, add $this.
(unless (member "static" (semantic-tag-get-attribute functiontag :typemodifiers))
(push (semantic-tag-new-variable "$this" functionparent nil) variabletags))
;; Find the parent class of the parent, this is the type of the
;; parent keyword.
(setq classparent (car-safe (semantic-tag-type-superclasses functionparent)))
(when classparent
(push (semantic-tag-new-variable "parent" classparent nil) variabletags))
;; Add self as variable (while not actually a variable).
(push (semantic-tag-new-variable "self" functionparent nil) variabletags))
(nreverse variabletags)))
(define-mode-local-override semantic-analyze-split-name
php-mode (name)
"Split up NAME by namespace parts."
;; Note: namespaces in PHP are not hierarchical so we need to figure
;; out a way to never walk the split name all the way down. We
;; actually only want to split the name in two pieces:
;; A\B\C -> A\B, C
;; but semantic calls this function repeatedly untill there's
;; nothing left to split, this is not easy to change.
(let ((parts (delete "" (split-string name "\\\\"))))
(if (= (length parts) 1)
(car parts)
parts)))
(define-mode-local-override semantic-find-tags-included
php-mode (&optional table)
"Find all include/require tags in TABLE.
TABLE is a tag table. See `semantic-something-to-tag-table'.
Override the default behaviour to look in members of toplevel
tags. Unlike in C, include statements can appear anywhere in a
file."
(semantic-find-tags-by-class 'include
(semantic-flatten-tags-table table)))
(define-mode-local-override semantic-documentation-for-tag
php-mode (&optional tag nosnarf)
"Find documentation from TAG and return it as a clean string.
TAG might have DOCUMENTATION set in it already. If not, there may be
some documentation in a comment preceding TAG's definition which we
can look for. When appropriate, this can be overridden by a language specific
enhancement.
Optional argument NOSNARF means to only return the lexical analyzer token for it.
If nosnarf if 'lex, then only return the lex token."
(let ((docstring (semantic-tag-get-attribute tag :documentation)))
(unless (stringp docstring)
(setq docstring (semantic-php-doc-snarf-comment-for-tag tag))
(semantic-tag-put-attribute tag :documentation docstring))
docstring))
(defun semantic-php-doc-snarf-comment-for-tag (tag)
"Extract a docstring from the docblock preceding TAG.
semantic-php does not use semantic-doc-snarf-comment-for-tag:
* snarf-comment-for-tag makes assumptions on the character
classes inside the documentation string, this is very error
prone and fails for common cases like when embedding URLs in
the comment
* semantic requires comment-end which is not set by cc-mode or php-mode"
(let ((docblock "")
(docstring ""))
(save-excursion
;; Find the tag.
(semantic-go-to-tag tag)
(beginning-of-line)
(backward-char)
;; Extract the docblock contents.
(when (looking-back "\*/")
(let ((docend (match-beginning 0))
docstart)
(when (search-backward "/\*\*" nil t)
(setq docstart (match-end 0)
docblock (buffer-substring-no-properties docstart docend))))))
;; Split the contents and produce a docstring.
(dolist (line (split-string docblock "\n" t) docstring)
(setq line (string-trim line))
(setq docstring (concat docstring
"\n"
(if (not (string= "*" line))
(string-remove-prefix "* " line)))))))
(provide 'semantic-php)
;;; semantic-php.el ends here