Eldoc and start of keywords for gff section.
[pico8.git] / pico8.el
index 3789d66..7600345 100644 (file)
--- a/pico8.el
+++ b/pico8.el
@@ -2,28 +2,30 @@
 ;;
 ;; Author: Joe Wreschnig <joe.wreschnig@gmail.com>
 ;; Package-Version: 20170620
-;; Package-Requires: ((emacs "25") (polymode "20170307") (lua-mode "20151025"))
+;; Package-Requires: ((emacs "25") (polymode "20170307") (lua-mode "20151025") (dash "2.12.0"))
 ;; URL: https://git.korewanetadesu.com/pico8.git
 ;; Keywords: convenience
 ;;
-;; This program 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 program 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.
 
 ;;; Commentary:
 ;;
-;; This mode (ab)uses polymode to fit six modes into one buffer, one
-;; of which is "real Lua" text and the other five of which have
-;; diverse strict formatting requirements.
+;; This mode (ab)uses polymode to fit six modes into one buffer, one of
+;; which is "real Lua" text and the other five of which have diverse
+;; strict formatting requirements.
 ;;
-;; It provides the keybinds and commands for inter-mode actions.
+;; It provides keybindings and commands for inter-mode actions.
 
 ;;; Code:
 
 (require 'polymode)
 (require 'lua-mode)
 (require 'thingatpt)
+(require 'dash)
+(require 'calc-bin)
 
 
 (defgroup pico8 nil
   :tag "PICO-8"
   :group 'languages)
 
-(defgroup pico8-faces nil
-  "Faces for PICO-8 (.p8) cartridge files."
-  :tag "PICO-8 Faces"
-  :group 'pico8)
-
 (defcustom pico8-executable-paths
   '("/Applications/PICO-8.app/Contents/MacOS/pico8" ; macOS
     "/usr/lib/pico8/pico8" ; PocketCHIP
@@ -50,8 +47,8 @@
 
 PICO-8 processes are long-lived with little surprising output, so
 their output buffers are killed by default when they exit.
-However, this is not usual behavior in Emacs, and can be
-disabled by setting this to t."
+However, this is not usual behavior in Emacs, and can be disabled
+by setting this to t."
   :group 'pico8
   :tag "Preserve PICO-8 Output On Exit"
   :type 'boolean)
@@ -59,9 +56,10 @@ disabled by setting this to t."
 (defcustom pico8-lua-indent-level 1
   "Default indentation for PICO-8 Lua mode.
 
-This overrides lua-indent-mode in `pico8-lua-mode'.  `lua-mode''s
-default indentation is 3, which is both idiosyncractic and quite
-large when viewed in the PICO-8 editor, where the convetion is 1."
+This overrides ‘lua-indent-level’ in ‘pico8-lua-mode’.
+‘lua-mode’ uses a default indentation is 3, which is both
+idiosyncratic and quite large when viewed in the PICO-8 editor,
+where the convention is 1."
   :group 'pico8
   :tag "PICO-8 Lua Indent Level")
 
@@ -87,10 +85,9 @@ large when viewed in the PICO-8 editor, where the convetion is 1."
 (defconst pico8-data-characters
   (append "0123456789abcdef" nil))
 
-(defface pico8-data-face
+(defface pico8-data
   '((t (:inherit fixed-pitch)))
-  "Face for PICO-8 binary data."
-  :group 'pico8-faces)
+  "Face for PICO-8 binary data.")
 
 (define-derived-mode pico8-data-mode fundamental-mode
   "PICO-8 Data"
@@ -108,13 +105,13 @@ insert the typed character if it is one of these characters,
 overwriting one of these characters."
   (interactive "P")
   (when (memq (char-after) pico8-data-characters)
-    (let ((overwrite-mode t))
+    (let ((overwrite-mode 'overwrite-mode-textual))
       (self-insert-command (prefix-numeric-value n)))))
 
 (let ((map pico8-data-mode-map))
   (suppress-keymap map)
-  (dolist (c pico8-data-characters)
-    (define-key map (format "%c" c) 'pico8-data-self-insert-command)))
+  (dolist (c (mapcar #'char-to-string pico8-data-characters))
+    (define-key map c 'pico8-data-self-insert-command)))
 
 (defun pico8-goto-char (position)
   "Set point to POSITION, a number.
@@ -129,7 +126,7 @@ widened if necessary to reach it."
   "Look up the PICO-8 executable."
   (or (car (delete nil (mapcar 'executable-find pico8-executable-paths)))
       (error "The PICO-8 executable could not be found.
-Make sure it is installed, and present in `pico8-executable-paths'")))
+Make sure it is installed, and present in ‘pico8-executable-paths’")))
 
 (defun pico8--create-output-buffer ()
   "Create and return a buffer for PICO-8 output."
@@ -260,58 +257,57 @@ Make sure it is installed, and present in `pico8-executable-paths'")))
           (col (current-column)))
       (+ (* 128 row) (/ col 2)))))
 
+(defun pico8-gff-eldoc ()
+  "Show information about the flag under the point."
+  (save-excursion
+    (when (cl-oddp (current-column))
+      (backward-char))
+    (when (looking-at "..")
+      (let* ((hex (substring-no-properties (match-string 0)))
+             (int (string-to-number hex 16)))
+        (format "%08d  %3d  %02x"
+                (let ((calc-number-radix 2))
+                  (string-to-number (math-format-radix int)))
+                int int)))))
+
+(defconst pico8-gff-font-lock-keywords
+  '(("..\n?" 0
+     (prog1 nil
+       ;; TODO: Lots of work to do here…
+       (let ((pdl (or (car (overlays-at (match-beginning 0)))
+                      (make-overlay (match-beginning 0) (match-end 0)))))
+         (overlay-put pdl 'after-string " "))))))
+
+
 (define-derived-mode pico8-gff-mode pico8-data-mode
   '(:eval (format "Flag[%d]" (pico8-gff-lighter)))
-  "Major mode for editing flags in PICO-8 cartridges.")
+  "Major mode for editing flags in PICO-8 cartridges."
+  (font-lock-add-keywords nil pico8-gff-font-lock-keywords)
+  (setq-local eldoc-documentation-function #'pico8-gff-eldoc))
 
 (defun pico8-gff-offset-of-flag (flag)
   "Calculate the offset of of flag number FLAG."
   (unless (<= 0 flag 255)
     (error "Valid flag numbers are 0 to 255, inclusive"))
   (+ (* 2 flag) (if (> flag 128) 1 0)))
-\f
-(defgroup pico8-pixel-faces nil
-  "Font faces to use for PICO-8 pixels.
-
-Rather than customizing each directly, you'll probably just want
-to change `pico8-pixel-face'."
-  :tag "PICO-8 Pixel Faces"
-  :group 'pico8-faces)
-
-(defface pico8-pixel-face
-  '((t (:inherit pico8-data-face :height 100)))
-  "Face for PICO-8 sprite 'pixels'."
-  :group 'pico8-faces)
 
-(dotimes (i (length pico8-colors))
-  (let ((c (nth i pico8-colors)))
-    (eval `(defface ,(intern (format "pico8-pixel-%x" i))
-             '((t (:inherit pico8-pixel-face :foreground ,c)))
-             ,(format "Face for PICO-8 sprite 'pixel' %x" i)
-             :group 'pico8-pixel-faces
-             :tag ,(format "Face for PICO-8 sprite 'pixel' %x." i)))))
+\f
+(defface pico8-pixel
+  '((t (:inherit pico8-data :height 100)))
+  "Face for PICO-8 sprite “pixels.”"
+  :group 'pico8)
 
 (defconst pico8-gfx-font-lock-keywords
-  `(("0+" . 'pico8-pixel-0)
-    ("1+" . 'pico8-pixel-1)
-    ("2+" . 'pico8-pixel-2)
-    ("3+" . 'pico8-pixel-3)
-    ("4+" . 'pico8-pixel-4)
-    ("5+" . 'pico8-pixel-5)
-    ("6+" . 'pico8-pixel-6)
-    ("7+" . 'pico8-pixel-7)
-    ("8+" . 'pico8-pixel-8)
-    ("9+" . 'pico8-pixel-9)
-    ("a+" . 'pico8-pixel-a)
-    ("b+" . 'pico8-pixel-b)
-    ("c+" . 'pico8-pixel-c)
-    ("d+" . 'pico8-pixel-d)
-    ("e+" . 'pico8-pixel-e)
-    ("f+" . 'pico8-pixel-f)
-
-    ;; If the \n isn't in the smaller face the line is taller to
-    ;; accomodate the full sized point at the end-of-line.
-    ("\n" . 'pico8-pixel-0)))
+  (cons
+   ;; If the \n isn't in the smaller face the line is taller to
+   ;; accommodate the full sized point at the end-of-line.
+   '("\n" . 'pico8-pixel)
+   (-map-indexed
+    (lambda (i c)
+      `(,(format "%x+" i)
+        0 '(face (:inherit pico8-pixel :foreground ,c))))
+    pico8-colors)))
+
 
 (defun pico8-gfx-current-position ()
   "Calculate the sprite and in-sprite position of the cursor."
@@ -369,14 +365,14 @@ to change `pico8-pixel-face'."
 (define-key pico8-gfx-mode-map "q" 'pico8-backward-sprite)
 (define-key pico8-gfx-mode-map "w" 'pico8-forward-sprite)
 
-(defface pico8-map-tile-face
-  '((t (:inherit pico8-data-face :height 100)))
+(defface pico8-map-tile
+  '((t (:inherit pico8-data :height 100)))
   "Face for PICO-8 map 'tiles'."
   :group 'pico8-faces)
 
 (defconst pico8-map-font-lock-keywords
-  '(("[0-9a-f]+" . 'pico8-map-tile-face)
-    ("\n" . 'pico8-map-tile-face)))
+  '(("[0-9a-f]+" . 'pico8-map-tile)
+    ("\n" . 'pico8-map-tile)))
 
 (defun pico8-map-lighter ()
   "Calculate the map tile under the cursor."
@@ -441,8 +437,8 @@ Returns nil, not 0, if the string was not converted."
   "Get the sprite number relevant to the point.
 
 When editing a flag, this is the flag number.  When editing a
-map, this is the value at the map.  When editing Lua code,
-this is the numeric literal in the code."
+map, this is the value at the map.  When editing Lua code, this
+is the numeric literal in the code."
   (cond
    ((derived-mode-p 'pico8-gff-mode) (pico8-gff-current-position))
 
@@ -453,9 +449,8 @@ this is the numeric literal in the code."
     (let ((beg (- (point) (% (min 255 (current-column)) 2))))
       (string-to-number (buffer-substring beg (+ beg 2)) 16)))
 
-   ;; In Lua or other code, the sprite is a numeric literal.
-   ;; lua-mode doesn't derive from anything.
-   ((or (derived-mode-p 'lua-mode) (derived-mode-p 'prog-mode))
+   ;; In Lua, the sprite is a numeric literal.
+   ((derived-mode-p 'lua-mode)
     (pico8--string-to-number (or (word-at-point) "")))
 
    (t nil)))
@@ -465,8 +460,8 @@ this is the numeric literal in the code."
   "Go to the sprite number relevant to the text at the point.
 
 When editing a flag, this is the flag number.  When editing a
-map, this is the value at the map.  When editing Lua code,
-this is the numeric literal in the code."
+map, this is the value at the map.  When editing Lua code, this
+is the numeric literal in the code."
   (interactive)
   (let ((sprite (pico8-sprite-relevant-to-point)))
     (if sprite (pico8-goto-sprite sprite)
@@ -495,9 +490,6 @@ This function needs a lot of work.."
                     (and (re-search-backward "\\<f[gs]et\\|s?spr\\>"
                                              (- (point) 30) t)
                          (char-after)))))
-           ;; FIXME: Actually parse something? lua-mode's sexp
-           ;; commands don't seem too good, and counting parentheses
-           ;; by hand is for nerds.
            (cond ((= c ?f) (pico8-goto-flag n))
                  ((= c ?s) (pico8-goto-sprite n))
                  (t (error "There's nothing obvious to go to")))))
@@ -538,7 +530,7 @@ This function needs a lot of work.."
 
   (flycheck-define-checker pico8-lua
     "A PICO-8 Lua syntax checker using the Lua compiler.
-See URL `http://www.lua.org/'."
+See URL http://www.lua.org/'."
     :command ("luac" "-p" source)
     :standard-input nil
     :error-patterns
@@ -550,7 +542,7 @@ See URL `http://www.lua.org/'."
 
   (flycheck-define-checker pico8-luacheck
     "A PICO-8 Lua syntax checker using luacheck.
-See URL `https://github.com/mpeterv/luacheck'."
+See URL ‘https://github.com/mpeterv/luacheck’."
     :command ("luacheck"
               "--formatter" "plain"
               "--codes"                   ; Show warning codes
@@ -567,7 +559,7 @@ See URL `https://github.com/mpeterv/luacheck'."
      (error line-start
             (optional (file-name))
             ":" line ":" column ":"
-            ;; `luacheck' before 0.11.0 did not output codes for errors, hence
+            ;; ‘luacheck’ before 0.11.0 did not output codes for errors, hence
             ;; the ID is optional here
             (optional " (" (id "E" (one-or-more digit)) ") ")
             (message) line-end))
@@ -617,3 +609,7 @@ See URL `https://github.com/mpeterv/luacheck'."
 
 (provide 'pico8)
 ;;; pico8.el ends here
+
+;; Local Variables:
+;; sentence-end-double-space: t
+;; End: