Skip to content

Commit c1f9e6b

Browse files
committed
Merge pull request #688 from geraldus/ac-improvements
Completion mechanics haskell-completions-grab-prefix implementation
2 parents 2512af2 + 92bd153 commit c1f9e6b

File tree

3 files changed

+478
-2
lines changed

3 files changed

+478
-2
lines changed

haskell-completions.el

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,19 @@
2626
;; This package provides completions related functionality for
2727
;; Haskell Mode such grab completion prefix at point, and etc..
2828

29+
;; Some description
30+
;; ================
31+
;;
32+
;; For major use function `haskell-completions-grab-prefix' is supposed, and
33+
;; other prefix grabbing functions are used internally by it. So, only this
34+
;; funciton have prefix minimal length functionality and invokes predicate
35+
;; function `haskell-completions-can-grab-prefix'.
36+
2937
;;; Code:
3038

39+
(require 'haskell-mode)
40+
41+
3142
(defun haskell-completions-can-grab-prefix ()
3243
"Check if the case is appropriate for grabbing completion prefix.
3344
Returns t if point is either at whitespace character, or at
@@ -42,5 +53,146 @@ whitespace or new line, otherwise returns nil.
4253
(backward-char)
4354
(not (looking-at (rx (| space line-end)))))))))
4455

56+
(defun haskell-completions-grab-pragma-prefix ()
57+
"Grab completion prefix for pragma completions.
58+
Returns a list of form '(prefix-start-position
59+
prefix-end-position prefix-value prefix-type) for pramga names
60+
such as WARNING, DEPRECATED, LANGUAGE and etc. Also returns
61+
completion prefixes for options in case OPTIONS_GHC pragma, or
62+
language extensions in case of LANGUAGE pragma. Obsolete OPTIONS
63+
pragma is supported also."
64+
(when (nth 4 (syntax-ppss))
65+
;; We're inside comment
66+
(let ((p (point))
67+
(comment-start (nth 8 (syntax-ppss)))
68+
(case-fold-search nil)
69+
prefix-start
70+
prefix-end
71+
prefix-type
72+
prefix-value)
73+
(save-excursion
74+
(goto-char comment-start)
75+
(when (looking-at (rx "{-#" (1+ (| space "\n"))))
76+
(let ((pragma-start (match-end 0)))
77+
(when (> p pragma-start)
78+
;; point stands after `{-#`
79+
(goto-char pragma-start)
80+
(when (looking-at (rx (1+ (| upper "_"))))
81+
;; found suitable sequence for pragma name
82+
(let ((pragma-end (match-end 0))
83+
(pragma-value (match-string-no-properties 0)))
84+
(if (eq p pragma-end)
85+
;; point is at the end of (in)complete pragma name
86+
;; prepare resulting values
87+
(progn
88+
(setq prefix-start pragma-start)
89+
(setq prefix-end pragma-end)
90+
(setq prefix-value pragma-value)
91+
(setq prefix-type
92+
'haskell-completions-pragma-name-prefix))
93+
(when (and (> p pragma-end)
94+
(or (equal "OPTIONS_GHC" pragma-value)
95+
(equal "OPTIONS" pragma-value)
96+
(equal "LANGUAGE" pragma-value)))
97+
;; point is after pragma name, so we need to check
98+
;; special cases of `OPTIONS_GHC` and `LANGUAGE` pragmas
99+
;; and provide a completion prefix for possible ghc
100+
;; option or language extension.
101+
(goto-char pragma-end)
102+
(when (re-search-forward
103+
(rx (* anything)
104+
(1+ (regexp "\\S-")))
105+
p
106+
t)
107+
(let* ((str (match-string-no-properties 0))
108+
(split (split-string str (rx (| space "\n")) t))
109+
(val (car (last split)))
110+
(end (point)))
111+
(when (and (equal p end)
112+
(not (string-match-p "#" val)))
113+
(setq prefix-value val)
114+
(backward-char (length val))
115+
(setq prefix-start (point))
116+
(setq prefix-end end)
117+
(setq
118+
prefix-type
119+
(if (not (equal "LANGUAGE" pragma-value))
120+
'haskell-completions-ghc-option-prefix
121+
'haskell-completions-language-extension-prefix
122+
)))))))))))))
123+
(when prefix-value
124+
(list prefix-start prefix-end prefix-value prefix-type)))))
125+
126+
(defun haskell-completions-grab-identifier-prefix ()
127+
"Grab completion prefix for identifier at point.
128+
Returns a list of form '(prefix-start-position
129+
prefix-end-position prefix-value prefix-type) for haskell
130+
identifier at point depending on result of function
131+
`haskell-ident-pos-at-point'."
132+
(let ((pos-at-point (haskell-ident-pos-at-point))
133+
(p (point)))
134+
(when pos-at-point
135+
(let* ((start (car pos-at-point))
136+
(end (cdr pos-at-point))
137+
(type 'haskell-completions-identifier-prefix)
138+
(case-fold-search nil)
139+
value)
140+
;; we need end position of result, becase of
141+
;; `haskell-ident-pos-at-point' ignores trailing whitespace, e.g. the
142+
;; result will be same for `map|` and `map |` invocations.
143+
(when (<= p end)
144+
(setq end p)
145+
(setq value (buffer-substring-no-properties start end))
146+
(when (string-match-p (rx bos upper) value)
147+
;; we need to check if found identifier is a module name
148+
(save-excursion
149+
(goto-char (line-beginning-position))
150+
(when (re-search-forward
151+
(rx "import"
152+
(? (1+ space) "qualified")
153+
(1+ space)
154+
upper
155+
(1+ (| alnum ".")))
156+
p ;; bound
157+
t) ;; no-error
158+
(if (equal p (point))
159+
(setq type 'haskell-completions-module-name-prefix)
160+
(when (re-search-forward
161+
(rx (| " as " "("))
162+
start
163+
t)
164+
;; but uppercase ident could occur after `as` keyword, or in
165+
;; module imports after opening parenthesis, in this case
166+
;; restore identifier type again, it's neccessary to
167+
;; distinguish the means of completions retrieval
168+
(setq type 'haskell-completions-identifier-prefix))))))
169+
(when (nth 8 (syntax-ppss))
170+
;; eighth element of syntax-ppss result is string or comment start,
171+
;; so when it's not nil word at point is inside string or comment,
172+
;; return special literal prefix type
173+
(setq type 'haskell-completions-general-prefix))
174+
;; finally take in account minlen if given and return the result
175+
(when value (list start end value type)))))))
176+
177+
(defun haskell-completions-grab-prefix (&optional minlen)
178+
"Grab prefix at point for possible completion.
179+
Returns a list of form '(prefix-start-position
180+
prefix-end-position prefix-value prefix-type) depending on
181+
situation, e.g. is it needed to complete pragma, module name,
182+
arbitrary identifier, and etc. Rerurns nil in case it is
183+
impossible to grab prefix.
184+
185+
If provided optional MINLEN parameter this function will return
186+
result only if prefix length is not less than MINLEN."
187+
(when (haskell-completions-can-grab-prefix)
188+
(let ((prefix (cond
189+
((haskell-completions-grab-pragma-prefix))
190+
((haskell-completions-grab-identifier-prefix)))))
191+
(cond ((and minlen prefix)
192+
(when (>= (length (nth 2 prefix)) minlen)
193+
prefix))
194+
(prefix prefix)))))
195+
196+
45197
(provide 'haskell-completions)
46198
;;; haskell-completions.el ends here

haskell.el

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@
7474
(end (match-end 1)))
7575
(list start end
7676
(haskell-process-get-repl-completions process text))))
77-
;; Complete OPTIONS using :complete repl ":set ..."
77+
;; Complete OPTIONS, a completion list comes from variable
78+
;; `haskell-ghc-supported-options'
7879
((and (nth 4 (syntax-ppss))
7980
(save-excursion
8081
(let ((p (point)))
@@ -84,7 +85,8 @@
8485
(rx symbol-start "-" (* (char alnum ?-)))
8586
(line-beginning-position)))
8687
(list (match-beginning 0) (match-end 0) haskell-ghc-supported-options))
87-
;; Complete LANGUAGE :complete repl ":set -X..."
88+
;; Complete LANGUAGE, a list of completions comes from variable
89+
;; `haskell-ghc-supported-options'
8890
((and (nth 4 (syntax-ppss))
8991
(save-excursion
9092
(let ((p (point)))

0 commit comments

Comments
 (0)