Skip to content

Commit af3483c

Browse files
committed
Remove arg string
1 parent 047c2cb commit af3483c

File tree

1 file changed

+204
-45
lines changed

1 file changed

+204
-45
lines changed

eldoc-meta-net.el

Lines changed: 204 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
;; Description: Eldoc support for meta-net
88
;; Keyword: eldoc c# dotnet sdk
99
;; Version: 0.1.0
10-
;; Package-Requires: ((emacs "25.1") (meta-net "1.1.0"))
10+
;; Package-Requires: ((emacs "25.1") (meta-net "1.1.0") (ht "2.3"))
1111
;; URL: https://github.com/emacs-vs/eldoc-meta-net
1212

1313
;; This file is NOT part of GNU Emacs.
@@ -32,30 +32,112 @@
3232

3333
;;; Code:
3434

35+
(require 'cl-lib)
3536
(require 'pcase)
3637
(require 'subr-x)
3738

3839
(require 'eldoc)
3940
(require 'meta-net)
41+
(require 'ht)
4042

4143
(defgroup eldoc-meta-net nil
4244
"Eldoc support for meta-net."
4345
:prefix "eldoc-meta-net-"
4446
:group 'tool
4547
:link '(url-link :tag "Repository" "https://github.com/emacs-vs/eldoc-meta-net"))
4648

49+
;; These keywords are grab from `csharp-mode'
50+
(defconst eldoc-meta-net--csharp-keywords
51+
(append
52+
'("class" "interface" "struct")
53+
'("bool" "byte" "sbyte" "char" "decimal" "double" "float" "int" "uint"
54+
"long" "ulong" "short" "ushort" "void" "object" "string" "var")
55+
'("typeof" "is" "as")
56+
'("enum" "new")
57+
'("using")
58+
'("abstract" "default" "final" "native" "private" "protected"
59+
"public" "partial" "internal" "readonly" "static" "event" "transient"
60+
"volatile" "sealed" "ref" "out" "virtual" "implicit" "explicit"
61+
"fixed" "override" "params" "async" "await" "extern" "unsafe"
62+
"get" "set" "this" "const" "delegate")
63+
'("select" "from" "where" "join" "in" "on" "equals" "into"
64+
"orderby" "ascending" "descending" "group" "when"
65+
"let" "by" "namespace")
66+
'("do" "else" "finally" "try")
67+
'("for" "if" "switch" "while" "catch" "foreach" "fixed" "checked"
68+
"unchecked" "using" "lock")
69+
'("break" "continue" "goto" "throw" "return" "yield")
70+
'("true" "false" "null" "value")
71+
'("base" "operator"))
72+
"Some C# keywords to eliminate namespaces.")
73+
74+
(defvar-local eldoc-meta-net--namespaces nil
75+
"Where store all the parsed namespaces.")
76+
77+
(defvar eldoc-meta-net-show-debug nil
78+
"Show the debug message from this package.")
79+
4780
;;
4881
;; (@* "Util" )
4982
;;
5083

84+
(defun eldoc-meta-net-debug (fmt &rest args)
85+
"Debug message like function `message' with same argument FMT and ARGS."
86+
(when eldoc-meta-net-show-debug (apply 'message fmt args)))
87+
5188
(defun eldoc-meta-net--inside-comment-p ()
5289
"Return non-nil if it's inside comment."
5390
(nth 4 (syntax-ppss)))
5491

92+
(defun eldoc-meta-net--inside-comment-or-string-p ()
93+
"Return non-nil if it's inside comment or string."
94+
(or (eldoc-meta-net--inside-comment-p) (nth 8 (syntax-ppss))))
95+
96+
(defun eldoc-meta-net--recursive-count (regex string &optional start)
97+
"Count REGEX in STRING; return an integer value.
98+
99+
Optional argument START is use for recursive counting."
100+
(unless start (setq start 0))
101+
(if (string-match regex string start)
102+
(+ 1 (eldoc-meta-net--recursive-count regex string (match-end 0)))
103+
0))
104+
105+
;;
106+
;; (@* "Xmls" )
107+
;;
108+
109+
(defvar-local eldoc-meta-net--xmls nil
110+
"Cache records a list of assembly xml file path.")
111+
112+
(defun eldoc-meta-net--all-xmls (&optional refresh)
113+
"Return full list of assembly xml files.
114+
If REFRESH is non-nil, refresh cache once."
115+
(when (or refresh (null eldoc-meta-net--xmls))
116+
(setq eldoc-meta-net--xmls (meta-net-csproj-xmls meta-net-csproj-current))
117+
(cl-delete-duplicates eldoc-meta-net--xmls))
118+
eldoc-meta-net--xmls)
119+
55120
;;
56121
;; (@* "Core" )
57122
;;
58123

124+
(defun eldoc-meta-net--grab-namespaces ()
125+
"Parsed namespaces from current buffer."
126+
(setq eldoc-meta-net--namespaces nil)
127+
(save-excursion
128+
(goto-char (point-min))
129+
(while (re-search-forward "[,.:]*[ \t\n]*\\([a-z-A-Z0-9_-]+\\)[ \t\n]*[.;{]+" nil t)
130+
(save-excursion
131+
(forward-symbol -1)
132+
(unless (eldoc-meta-net--inside-comment-or-string-p)
133+
(when-let ((symbol (thing-at-point 'symbol)))
134+
(push symbol eldoc-meta-net--namespaces))))))
135+
(setq eldoc-meta-net--namespaces (reverse eldoc-meta-net--namespaces)
136+
eldoc-meta-net--namespaces (delete-dups eldoc-meta-net--namespaces)
137+
eldoc-meta-net--namespaces (cl-remove-if (lambda (namespace)
138+
(member namespace eldoc-meta-net--csharp-keywords))
139+
eldoc-meta-net--namespaces)))
140+
59141
(defun eldoc-meta-net--possible-function-point ()
60142
"This function get called infront of the opening curly bracket.
61143
@@ -102,37 +184,6 @@ This function also ignore generic type between < and >."
102184
(eldoc-meta-net--possible-function-point)
103185
(unless (eldoc-meta-net--inside-comment-p) (thing-at-point 'symbol))))
104186

105-
(defun eldoc-meta-net--arg-string ()
106-
"Return argument string."
107-
(save-excursion
108-
(when (search-forward "(" nil t)
109-
(forward-char -1)
110-
(let ((beg (point)) arg-string)
111-
(forward-sexp 1)
112-
(setq arg-string (buffer-substring beg (point)))
113-
;; Here we remove everything inside nested arguments
114-
;;
115-
;; For example,
116-
;;
117-
;; Add(a, b, (x, y, z) => { }, c)
118-
;;
119-
;; should return,
120-
;;
121-
;; (a, b, => , c)
122-
;;
123-
;; Of course, if you are inside the x y z scope then it would just
124-
;; return (x, y, z). This is fine since ElDoc should just only need
125-
;; to know the first layer's function.
126-
(with-temp-buffer
127-
(insert arg-string)
128-
(goto-char (1+ (point-min)))
129-
(while (re-search-forward "[({]" nil t)
130-
(forward-char -1)
131-
(let ((start (point)))
132-
(forward-sexp 1)
133-
(delete-region start (point))))
134-
(buffer-string))))))
135-
136187
(defun eldoc-meta-net--arg-boundaries ()
137188
"Return a list of cons cell represent arguments' boundaries.
138189
@@ -146,34 +197,142 @@ For example, (^ is start; $ is end)
146197
147198
This function also get called infront of the opening curly bracket. See
148199
function `eldoc-meta-net--possible-function-point' for the graph."
149-
(let (boundaries start (max-pt (save-excursion (forward-sexp 1))))
200+
(let (bounds start (max-pt (save-excursion (forward-sexp 1))))
150201
(save-excursion
151202
(forward-char 1)
152203
(setq start (point))
153-
(while (re-search-forward "[,{()]" max-pt t)
204+
(while (re-search-forward "[,<{[()]" max-pt t)
154205
(pcase (string (char-before))
155206
((or "," ")")
156-
(push (cons start (1- (point))) boundaries)
207+
(push (cons start (1- (point))) bounds)
157208
(setq start (point)))
158-
((or "{" "(") (forward-char -1) (forward-sexp 1)))))
159-
(reverse boundaries)))
209+
((or "{" "(" "<" "[") (forward-char -1) (forward-sexp 1)))))
210+
(reverse bounds)))
211+
212+
(defun eldoc-meta-net-current-index (bounds point)
213+
"Return the index of current boundary from BOUNDS.
214+
215+
Argument POINT is the currnet check point."
216+
(cl-position-if
217+
(lambda (bound)
218+
(let ((start (car bound)) (end (cdr bound)))
219+
(and (<= start point) (<= point end))))
220+
bounds))
221+
222+
(defun eldoc-meta-net--match-name (type)
223+
"Return non-nil, if the TYPE match the current namespace list.
224+
225+
The argument TYPE is a list of namespace in string. For instance,
226+
227+
using System.Collections; => '(System Collections)
228+
229+
We use this to eliminate not possible candidates."
230+
(let ((match t) (len (length type)) (index 0) item)
231+
(while (and match (< index len))
232+
(setq item (nth index type)
233+
index (1+ index)
234+
match (member item eldoc-meta-net--namespaces)))
235+
match))
236+
237+
(defun eldoc-meta-net--grab-data (function-name)
238+
"Return data that matches FUNCTION-NAME."
239+
(unless meta-net-csproj-current (meta-net-read-project)) ; read it
240+
(eldoc-meta-net--grab-namespaces) ; first grab the data from `meta-net'
241+
242+
(let* ((xmls (eldoc-meta-net--all-xmls)) ; Get the list of xml files from current project
243+
(xmls-len (length xmls)) ; length of the xmls
244+
(xml-index 0) ; index search through all `xmls`
245+
xml ; current xml path as key
246+
break ; flag to stop
247+
type ; xml assembly type
248+
comp-name ; name of the type, the last component from the type
249+
splits ; temporary list to chop namespace, use to produce `comp-name`
250+
namespaces
251+
result)
252+
(while (and (not break) (< xml-index xmls-len))
253+
(setq xml (nth xml-index xmls)
254+
xml-index (1+ xml-index))
255+
(let* ((types (meta-net-xml-types xml))
256+
(types-len (length types))
257+
(type-index 0))
258+
(while (and (not break) (< type-index types-len))
259+
(setq type (nth type-index types)
260+
type-index (1+ type-index)
261+
splits (split-string type "\\.")
262+
comp-name (nth (1- (length splits)) splits)
263+
namespaces (butlast splits))
264+
;; Check if all namespaces appears in the buffer,
265+
;;
266+
;; We use `butlast' to get rid of the component name because we do
267+
;; allow the same level candidates.
268+
;;
269+
;; For example, `NamespaceA` contains `classA` and `classB`, and we
270+
;; ignore the check of `classA` and `classB` in order to let them
271+
;; both appears in candidates list.
272+
(when (eldoc-meta-net--match-name namespaces)
273+
(eldoc-meta-net-debug "\f")
274+
(eldoc-meta-net-debug "xml: %s" xml)
275+
(eldoc-meta-net-debug "Type: %s" type)
276+
(eldoc-meta-net-debug "Name: %s" comp-name)
277+
(when-let* ((methods (meta-net-type-methods xml type))
278+
(methods-keys (ht-keys methods)))
279+
(dolist (key methods-keys) ; `key` is function name with arguments
280+
(when (string-match-p (format "\\_<%s\\_>" function-name) key)
281+
(push (cons key (ht-get methods key)) result)
282+
(setq break t))))))))
283+
result))
160284

161285
(defun eldoc-meta-net-function ()
162286
"Main eldoc entry."
163287
(save-excursion
164-
(when-let* ((function-name (eldoc-meta-net--function-name))
165-
(arg-string (eldoc-meta-net--arg-string))
288+
(when-let* ((start (point))
289+
(function-name (eldoc-meta-net--function-name))
166290
(arg-bounds (eldoc-meta-net--arg-boundaries)) ; list of cons cell
167-
(arg-count (length arg-bounds)))
168-
(jcs-print function-name)
169-
(jcs-print arg-string)
170-
(jcs-print arg-bounds)
171-
(jcs-print arg-count)
291+
(arg-index (eldoc-meta-net-current-index arg-bounds start))
292+
(arg-count (length arg-bounds))
293+
(methods (eldoc-meta-net--grab-data function-name)))
294+
(eldoc-meta-net-debug "Funtion name: %s" function-name)
295+
(eldoc-meta-net-debug "Arg Bounds: %s" arg-bounds)
296+
(eldoc-meta-net-debug "Arg Count: k%s" arg-count)
297+
;; Find match arguments count, for function overloading
298+
(let ((index 0) (len (length methods)) data break
299+
name info match-arg-count target)
300+
(while (and (not break) (< index len))
301+
(setq data (nth index methods)
302+
index (1+ index)
303+
name (car data) info (cdr data)
304+
match-arg-count 0)
305+
(with-temp-buffer
306+
(insert name)
307+
(goto-char (point-min))
308+
(when (search-forward "(" nil t)
309+
(forward-char -1)
310+
(setq match-arg-count (length (eldoc-meta-net--arg-boundaries)))
311+
;; Notice that function overloading can has same argument count
312+
;; but with different type.
313+
;;
314+
;; For instance,
315+
;; ---
316+
;; OverloadingFunction(System.String)
317+
;; ---
318+
;; and,
319+
;; ---
320+
;; OverloadingFunction(System.Type)
321+
;; ---
322+
;;
323+
;; Since we cannot know the type from the uesr, we just pick one
324+
;; that matches.
325+
(when (= match-arg-count arg-count)
326+
(setq target data ; found
327+
break t))))
328+
(jcs-print name)
329+
(jcs-print (ht-get info 'summary))
330+
(jcs-print "Match arg count: "match-arg-count))
331+
name)
172332
)))
173333

174334
(defun eldoc-meta-net--turn-on ()
175335
"Start the `eldoc-meta-net' worker."
176-
(unless meta-net-csproj-current (meta-net-read-project))
177336
(add-function :before-until (local 'eldoc-documentation-function) #'eldoc-meta-net-function)
178337
(eldoc-mode 1))
179338

0 commit comments

Comments
 (0)