final tt updates
[emacs-init.git] / elpa / kotlin-mode-20190116.2055 / kotlin-mode.el
1 ;;; kotlin-mode.el --- Major mode for kotlin -*- lexical-binding: t; -*-
2
3 ;; Copyright © 2015  Shodai Yokoyama
4
5 ;; Author: Shodai Yokoyama (quantumcars@gmail.com)
6 ;; Keywords: languages
7 ;; Package-Version: 20190116.2055
8 ;; Package-Requires: ((emacs "24.3"))
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24
25 ;;
26
27 ;;; Code:
28
29 (require 'comint)
30 (require 'rx)
31 (require 'cc-cmds)
32
33 (defgroup kotlin nil
34   "A Kotlin major mode."
35   :group 'languages)
36
37 (defcustom kotlin-tab-width tab-width
38   "The tab width to use for indentation."
39   :type 'integer
40   :group 'kotlin-mode
41   :safe 'integerp)
42
43 (defcustom kotlin-command "kotlinc"
44   "The Kotlin command used for evaluating code."
45   :type 'string
46   :group 'kotlin)
47
48 (defcustom kotlin-args-repl '()
49   "The arguments to pass to `kotlin-command' to start a REPL."
50   :type 'list
51   :group 'kotlin)
52
53 (defcustom kotlin-repl-buffer "*KotlinREPL*"
54   "The name of the KotlinREPL buffer."
55   :type 'string
56   :group 'kotlin)
57
58 (defun kotlin-do-and-repl-focus (f &rest args)
59   (apply f args)
60   (pop-to-buffer kotlin-repl-buffer))
61
62 (defun kotlin-send-region (start end)
63   "Send current region to Kotlin interpreter."
64   (interactive "r")
65   (comint-send-region kotlin-repl-buffer start end)
66   (comint-send-string kotlin-repl-buffer "\n"))
67
68 (defun kotlin-send-region-and-focus (start end)
69   "Send current region to Kotlin interpreter and switch to it."
70   (interactive "r")
71   (kotlin-do-and-repl-focus 'kotlin-send-region start end))
72
73 (defun kotlin-send-buffer ()
74   "Send whole buffer to Kotlin interpreter."
75   (interactive)
76   (kotlin-send-region (point-min) (point-max)))
77
78 (defun kotlin-send-buffer-and-focus ()
79   "Send whole buffer to Kotlin interpreter and switch to it."
80   (interactive)
81   (kotlin-do-and-repl-focus 'kotlin-send-buffer))
82
83 (defun kotlin-send-block ()
84   (interactive)
85   (let* ((p (point)))
86     (mark-paragraph)
87     (kotlin-send-region (region-beginning) (region-end))
88     (goto-char p)))
89
90 (defun kotlin-send-block-and-focus ()
91   "Send block to Kotlin interpreter and switch to it."
92   (interactive)
93   (kotlin-do-and-repl-focus 'kotlin-send-block))
94
95 (defun kotlin-send-line ()
96   (interactive)
97   (kotlin-send-region
98    (line-beginning-position)
99    (line-end-position)))
100
101 (defun kotlin-send-line-and-focus ()
102   "Send current line to Kotlin interpreter and switch to it."
103   (interactive)
104   (kotlin-do-and-repl-focus 'kotlin-send-line))
105
106 (defun kotlin-repl ()
107   "Launch a Kotlin REPL using `kotlin-command' as an inferior mode."
108   (interactive)
109
110   (unless (comint-check-proc kotlin-repl-buffer)
111     (set-buffer
112      (apply 'make-comint "KotlinREPL"
113             "env"
114             nil
115             "NODE_NO_READLINE=1"
116             kotlin-command
117             kotlin-args-repl))
118
119     (set (make-local-variable 'comint-preoutput-filter-functions)
120          (cons (lambda (string)
121                  (replace-regexp-in-string "\x1b\\[.[GJK]" "" string)) nil)))
122
123   (pop-to-buffer kotlin-repl-buffer))
124
125 (defvar kotlin-mode-map
126   (let ((map (make-keymap)))
127     (define-key map (kbd "C-c C-z") 'kotlin-repl)
128     (define-key map (kbd "C-c C-n") 'kotlin-send-line)
129     (define-key map (kbd "C-c C-r") 'kotlin-send-region)
130     (define-key map (kbd "C-c C-c") 'kotlin-send-block)
131     (define-key map (kbd "C-c C-b") 'kotlin-send-buffer)
132     (define-key map (kbd "<tab>") 'c-indent-line-or-region)
133     map)
134   "Keymap for kotlin-mode")
135
136 (defvar kotlin-mode-syntax-table
137   (let ((st (make-syntax-table)))
138
139     ;; Strings
140     (modify-syntax-entry ?\" "\"" st)
141
142     ;; `_' as being a valid part of a word
143     (modify-syntax-entry ?_ "w" st)
144
145     ;; b-style comment
146     (modify-syntax-entry ?/ ". 124b" st)
147     (modify-syntax-entry ?* ". 23" st)
148     (modify-syntax-entry ?\n "> b" st)
149     st))
150
151
152 ;;; Font Lock
153
154 (defconst kotlin-mode--misc-keywords
155   '("package" "import"))
156
157 (defconst kotlin-mode--type-decl-keywords
158   '("nested" "inner" "data" "class" "interface" "trait" "typealias" "enum" "object"))
159
160 (defconst kotlin-mode--fun-decl-keywords
161   '("fun"))
162
163 (defconst kotlin-mode--val-decl-keywords
164   '("val" "var"))
165
166 (defconst kotlin-mode--statement-keywords
167   '(;; Branching
168     "if" "else"
169     ;; Exceptions
170     "try" "catch" "finally" "throw"
171     ;; Loops
172     "while" "for" "do" "continue" "break"
173     ;; Miscellaneous
174     "when" "is" "in" "as" "return"))
175
176 (defconst kotlin-mode--context-variables-keywords
177   '("this" "super"))
178
179 (defvar kotlin-mode--keywords
180   (append kotlin-mode--misc-keywords
181           kotlin-mode--type-decl-keywords
182           kotlin-mode--fun-decl-keywords
183           kotlin-mode--val-decl-keywords
184           kotlin-mode--statement-keywords
185           kotlin-mode--context-variables-keywords)
186   "Keywords used in Kotlin language.")
187
188 (defconst kotlin-mode--constants-keywords
189   '("null" "true" "false"))
190
191 (defconst kotlin-mode--modifier-keywords
192   '("open" "private" "protected" "public" "lateinit"
193     "override" "abstract" "final" "companion"
194     "annotation" "internal" "const" "in" "out")) ;; "in" "out"
195
196 (defconst kotlin-mode--property-keywords
197   '("by")) ;; "by" "get" "set"
198
199 (defconst kotlin-mode--initializer-keywords
200   '("init" "constructor"))
201
202 (defvar kotlin-mode--font-lock-keywords
203   `(;; Keywords
204     (,(rx-to-string
205      `(and bow (group (or ,@kotlin-mode--keywords)) eow)
206      t)
207      1 font-lock-keyword-face)
208
209     ;; Package names
210     (,(rx-to-string
211        `(and (or ,@kotlin-mode--misc-keywords) (+ space)
212              (group (+ (any word ?.))))
213        t)
214      1 font-lock-string-face)
215
216     ;; Types
217     (,(rx-to-string
218       `(and bow upper (group (* (or word "<" ">" "." "?" "!" "*"))))
219       t)
220      0 font-lock-type-face)
221
222     ;; Classes/Enums
223     (,(rx-to-string
224       `(and bow (or ,@kotlin-mode--type-decl-keywords) eow (+ space)
225             (group (+ word)) eow)
226       t)
227      1 font-lock-type-face)
228
229     ;; Constants
230     (,(rx-to-string
231        `(and bow (group (or ,@kotlin-mode--constants-keywords)) eow)
232        t)
233      0 font-lock-constant-face)
234
235     ;; Value bindings
236     (,(rx-to-string
237        `(and bow (or ,@kotlin-mode--val-decl-keywords) eow
238              (+ space)
239              (group (+ word)) (* space)  (\? ":"))
240        t)
241      1 font-lock-variable-name-face t)
242
243     ;; Function names
244     (,(rx-to-string
245        `(and (or ,@kotlin-mode--fun-decl-keywords)
246              (+ space) bow (group (+ (any alnum word))) eow)
247        t)
248      1 font-lock-function-name-face)
249
250     ;; Access modifier
251     ;; Access modifier is valid identifier being used as variable
252     ;; TODO: Highlight only modifiers in the front of class/fun
253     (,(rx-to-string
254        `(and bow (group (or ,@kotlin-mode--modifier-keywords))
255              eow)
256        t)
257      1 font-lock-keyword-face)
258
259     ;; Properties
260     ;; by/get/set are valid identifiers being used as variable
261     ;; TODO: Highlight keywords in the property declaration statement
262     ;; (,(rx-to-string
263     ;;    `(and bow (group (or ,@kotlin-mode--property-keywords)) eow)
264     ;;    t)
265     ;;  1 font-lock-keyword-face)
266
267     ;; Constructor/Initializer blocks
268     (,(rx-to-string
269        `(and bow (group (or ,@kotlin-mode--initializer-keywords)) eow)
270        t)
271      1 font-lock-keyword-face)
272
273     ;; String interpolation
274     (kotlin-mode--match-interpolation 0 font-lock-variable-name-face t))
275   "Default highlighting expression for `kotlin-mode'")
276
277 (defun kotlin-mode--new-font-lock-keywords ()
278   '(
279     ("package\\|import" . font-lock-keyword-face)
280     ))
281
282 (defun kotlin-mode--syntax-propertize-interpolation ()
283   (let* ((pos (match-beginning 0))
284          (context (save-excursion
285                     (save-match-data (syntax-ppss pos)))))
286     (when (nth 3 context)
287       (put-text-property pos
288                          (1+ pos)
289                          'kotlin-property--interpolation
290                          (match-data)))))
291
292 (defun kotlin-mode--syntax-propertize-function (start end)
293   (let ((case-fold-search))
294     (goto-char start)
295     (remove-text-properties start end '(kotlin-property--interpolation))
296     (funcall
297      (syntax-propertize-rules
298       ((let ((identifier '(or
299                            (and alpha (* alnum))
300                            (and "`" (+ (not (any "`\n"))) "`"))))
301          (rx-to-string
302           `(or (group "${" ,identifier "}")
303                (group "$" ,identifier))))
304        (0 (ignore (kotlin-mode--syntax-propertize-interpolation)))))
305      start end)))
306
307 (defun kotlin-mode--match-interpolation (limit)
308   (let ((pos (next-single-char-property-change (point)
309                                                'kotlin-property--interpolation
310                                                nil
311                                                limit)))
312     (when (and pos (> pos (point)))
313       (goto-char pos)
314       (let ((value (get-text-property pos 'kotlin-property--interpolation)))
315         (if value
316             (progn (set-match-data value)
317                    t)
318           (kotlin-mode--match-interpolation limit))))))
319
320 (defun kotlin-mode--prev-line ()
321   "Moves up to the nearest non-empty line"
322   (if (not (bobp))
323       (progn
324         (forward-line -1)
325         (while (and (looking-at "^[ \t]*$") (not (bobp)))
326           (forward-line -1)))))
327
328 (defun kotlin-mode--indent-line ()
329   "Indent current line as kotlin code"
330   (interactive)
331   (beginning-of-line)
332   (if (bobp) ; 1.)
333       (progn
334         (kotlin-mode--beginning-of-buffer-indent))
335     (let ((not-indented t) cur-indent)
336       (cond ((looking-at "^[ \t]*\\.") ; line starts with .
337              (save-excursion
338                (kotlin-mode--prev-line)
339                (cond ((looking-at "^[ \t]*\\.")
340                       (setq cur-indent (current-indentation)))
341
342                      (t
343                       (setq cur-indent (+ (current-indentation) (* 2 kotlin-tab-width)))))
344                (if (< cur-indent 0)
345                    (setq cur-indent 0))))
346
347             ((looking-at "^[ \t]*}") ; line starts with }
348              (save-excursion
349                (kotlin-mode--prev-line)
350                (while (and (or (looking-at "^[ \t]*$") (looking-at "^[ \t]*\\.")) (not (bobp)))
351                  (kotlin-mode--prev-line))
352                (cond ((or (looking-at ".*{[ \t]*$") (looking-at ".*{.*->[ \t]*$"))
353                       (setq cur-indent (current-indentation)))
354                      (t
355                       (setq cur-indent (- (current-indentation) kotlin-tab-width)))))
356              (if (< cur-indent 0)
357                  (setq cur-indent 0)))
358
359             ((looking-at "^[ \t]*)") ; line starts with )
360              (save-excursion
361                (kotlin-mode--prev-line)
362                (setq cur-indent (- (current-indentation) kotlin-tab-width)))
363              (if (< cur-indent 0)
364                  (setq cur-indent 0)))
365
366             (t
367              (save-excursion
368                (while not-indented
369                  (kotlin-mode--prev-line)
370                  (cond ((looking-at ".*{[ \t]*$") ; line ends with {
371                         (setq cur-indent (+ (current-indentation) kotlin-tab-width))
372                         (setq not-indented nil))
373
374                        ((looking-at "^[ \t]*}") ; line starts with }
375                         (setq cur-indent (current-indentation))
376                         (setq not-indented nil))
377
378                        ((looking-at ".*{.*->[ \t]*$") ; line ends with ->
379                         (setq cur-indent (+ (current-indentation) kotlin-tab-width))
380                         (setq not-indented nil))
381
382                        ((looking-at ".*([ \t]*$") ; line ends with (
383                         (setq cur-indent (+ (current-indentation) kotlin-tab-width))
384                         (setq not-indented nil))
385
386                        ((looking-at "^[ \t]*).*$") ; line starts with )
387                         (setq cur-indent (current-indentation))
388                         (setq not-indented nil))
389
390                        ((bobp) ; 5.)
391                         (setq not-indented nil)))))))
392       (if cur-indent
393           (indent-line-to cur-indent)
394         (indent-line-to 0)))))
395
396
397 (defun kotlin-mode--beginning-of-buffer-indent ()
398   (indent-line-to 0))
399
400 ;;;###autoload
401 (define-derived-mode kotlin-mode prog-mode "Kotlin"
402   "Major mode for editing Kotlin."
403
404   (setq font-lock-defaults '((kotlin-mode--font-lock-keywords) nil nil))
405   (setq-local syntax-propertize-function #'kotlin-mode--syntax-propertize-function)
406   (set (make-local-variable 'comment-start) "//")
407   (set (make-local-variable 'comment-padding) 1)
408   (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *")
409   (set (make-local-variable 'comment-end) "")
410   (set (make-local-variable 'indent-line-function) 'kotlin-mode--indent-line)
411   (setq-local adaptive-fill-regexp comment-start-skip)
412
413   :group 'kotlin
414   :syntax-table kotlin-mode-syntax-table)
415
416 ;;;###autoload
417 (add-to-list 'auto-mode-alist '("\\.kts?\\'" . kotlin-mode) t)
418
419 (provide 'kotlin-mode)
420 ;;; kotlin-mode.el ends here