diff options
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-debug.el')
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-debug.el | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-debug.el b/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-debug.el new file mode 100644 index 000000000000..38a6859bbd2f --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/haskell-mode-20180601.143/haskell-debug.el @@ -0,0 +1,757 @@ +;;; haskell-debug.el --- Debugging mode via GHCi -*- lexical-binding: t -*- + +;; Copyright © 2014 Chris Done. All rights reserved. +;; 2016 Arthur Fayzrakhmanov + +;; 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, 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 program. If not, see <http://www.gnu.org/licenses/>. + +;;; Code: + +(require 'cl-lib) +(require 'haskell-session) +(require 'haskell-process) +(require 'haskell-interactive-mode) +(require 'haskell-font-lock) +(require 'haskell-utils) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Configuration + +;;;###autoload +(defgroup haskell-debug nil + "Settings for debugging support." + :link '(custom-manual "(haskell-mode)haskell-debug") + :group 'haskell) + +;;;###autoload +(defface haskell-debug-warning-face + '((t :inherit 'compilation-warning)) + "Face for warnings." + :group 'haskell-debug) + +;;;###autoload +(defface haskell-debug-trace-number-face + '((t :weight bold :background "#f5f5f5")) + "Face for numbers in backtrace." + :group 'haskell-debug) + +;;;###autoload +(defface haskell-debug-newline-face + '((t :weight bold :background "#f0f0f0")) + "Face for newlines in trace steps." + :group 'haskell-debug) + +;;;###autoload +(defface haskell-debug-keybinding-face + '((t :inherit 'font-lock-type-face :weight bold)) + "Face for keybindings." + :group 'haskell-debug) + +;;;###autoload +(defface haskell-debug-heading-face + '((t :inherit 'font-lock-keyword-face)) + "Face for headings." + :group 'haskell-debug) + +;;;###autoload +(defface haskell-debug-muted-face + '((t :foreground "#999")) + "Face for muteds." + :group 'haskell-debug) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Mode + +(defvar haskell-debug-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "g") 'haskell-debug/refresh) + (define-key map (kbd "s") 'haskell-debug/step) + (define-key map (kbd "t") 'haskell-debug/trace) + (define-key map (kbd "d") 'haskell-debug/delete) + (define-key map (kbd "b") 'haskell-debug/break-on-function) + (define-key map (kbd "a") 'haskell-debug/abandon) + (define-key map (kbd "c") 'haskell-debug/continue) + (define-key map (kbd "p") 'haskell-debug/previous) + (define-key map (kbd "n") 'haskell-debug/next) + (define-key map (kbd "RET") 'haskell-debug/select) + map) + "Keymap for `haskell-debug-mode'.") + +(define-derived-mode haskell-debug-mode + text-mode "Debug" + "Major mode for debugging Haskell via GHCi.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Globals + +(defvar haskell-debug-history-cache nil + "Cache of the tracing history.") + +(defvar haskell-debug-bindings-cache nil + "Cache of the current step's bindings.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Macros + +(defmacro haskell-debug-with-breakpoints (&rest body) + "Breakpoints need to exist to start stepping." + `(if (haskell-debug-get-breakpoints) + ,@body + (error "No breakpoints to step into!"))) + +(defmacro haskell-debug-with-modules (&rest body) + "Modules need to exist to do debugging stuff." + `(if (haskell-debug-get-modules) + ,@body + (error "No modules loaded!"))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Interactive functions + +(defun haskell-debug/select () + "Select whatever is at point." + (interactive) + (cond + ((get-text-property (point) 'break) + (let ((break (get-text-property (point) 'break))) + (haskell-debug-highlight (plist-get break :path) + (plist-get break :span)))) + ((get-text-property (point) 'module) + (let ((break (get-text-property (point) 'module))) + (haskell-debug-highlight (plist-get break :path)))))) + +(defun haskell-debug/abandon () + "Abandon the current computation." + (interactive) + (haskell-debug-with-breakpoints + (haskell-process-queue-sync-request (haskell-debug-process) ":abandon") + (message "Computation abandoned.") + (setq haskell-debug-history-cache nil) + (setq haskell-debug-bindings-cache nil) + (haskell-debug/refresh))) + +(defun haskell-debug/continue () + "Continue the current computation." + (interactive) + (haskell-debug-with-breakpoints + (haskell-process-queue-sync-request (haskell-debug-process) ":continue") + (message "Computation continued.") + (setq haskell-debug-history-cache nil) + (setq haskell-debug-bindings-cache nil) + (haskell-debug/refresh))) + +(defun haskell-debug/break-on-function () + "Break on function IDENT." + (interactive) + (haskell-debug-with-modules + (let ((ident (read-from-minibuffer "Function: " + (haskell-ident-at-point)))) + (haskell-process-queue-sync-request + (haskell-debug-process) + (concat ":break " + ident)) + (message "Breaking on function: %s" ident) + (haskell-debug/refresh)))) + +(defun haskell-debug/start-step (expr) + "Start stepping EXPR." + (interactive (list (read-from-minibuffer "Expression to step through: "))) + (haskell-debug/step expr)) + +(defun haskell-debug/breakpoint-numbers () + "List breakpoint numbers." + (interactive) + (let ((breakpoints (mapcar (lambda (breakpoint) + (number-to-string (plist-get breakpoint :number))) + (haskell-debug-get-breakpoints)))) + (if (null breakpoints) + (message "No breakpoints.") + (message "Breakpoint(s): %s" + (mapconcat #'identity + breakpoints + ", "))))) + +(defun haskell-debug/next () + "Go to next step to inspect bindings." + (interactive) + (haskell-debug-with-breakpoints + (haskell-debug-navigate "forward"))) + +(defun haskell-debug/previous () + "Go to previous step to inspect the bindings." + (interactive) + (haskell-debug-with-breakpoints + (haskell-debug-navigate "back"))) + +(defun haskell-debug/refresh () + "Refresh the debugger buffer." + (interactive) + (with-current-buffer (haskell-debug-buffer-name (haskell-debug-session)) + (cd (haskell-session-current-dir (haskell-debug-session))) + (let ((inhibit-read-only t) + (p (point))) + (erase-buffer) + (insert (propertize (concat "Debugging " + (haskell-session-name (haskell-debug-session)) + "\n\n") + 'face `((:weight bold)))) + (let ((modules (haskell-debug-get-modules)) + (breakpoints (haskell-debug-get-breakpoints)) + (context (haskell-debug-get-context)) + (history (haskell-debug-get-history))) + (unless modules + (insert (propertize "You have to load a module to start debugging." + 'face + 'haskell-debug-warning-face) + "\n\n")) + (haskell-debug-insert-bindings modules breakpoints context) + (when modules + (haskell-debug-insert-current-context context history) + (haskell-debug-insert-breakpoints breakpoints)) + (haskell-debug-insert-modules modules)) + (insert "\n") + (goto-char (min (point-max) p))))) + +(defun haskell-debug/delete () + "Delete whatever's at the point." + (interactive) + (cond + ((get-text-property (point) 'break) + (let ((break (get-text-property (point) 'break))) + (haskell-mode-toggle-interactive-prompt-state) + (unwind-protect + (when (y-or-n-p (format "Delete breakpoint #%d?" + (plist-get break :number))) + (haskell-process-queue-sync-request + (haskell-debug-process) + (format ":delete %d" + (plist-get break :number))) + (haskell-debug/refresh)) + (haskell-mode-toggle-interactive-prompt-state t)))))) + +(defun haskell-debug/trace () + "Trace the expression." + (interactive) + (haskell-debug-with-modules + (haskell-debug-with-breakpoints + (let ((expr (read-from-minibuffer "Expression to trace: " + (haskell-ident-at-point)))) + (haskell-process-queue-sync-request + (haskell-debug-process) + (concat ":trace " expr)) + (message "Tracing expression: %s" expr) + (haskell-debug/refresh))))) + +(defun haskell-debug/step (&optional expr) + "Step into the next function." + (interactive) + (haskell-debug-with-breakpoints + (let* ((breakpoints (haskell-debug-get-breakpoints)) + (context (haskell-debug-get-context)) + (string + (haskell-process-queue-sync-request + (haskell-debug-process) + (if expr + (concat ":step " expr) + ":step")))) + (cond + ((string= string "not stopped at a breakpoint\n") + (if haskell-debug-bindings-cache + (progn (setq haskell-debug-bindings-cache nil) + (haskell-debug/refresh)) + (call-interactively 'haskell-debug/start-step))) + (t (let ((maybe-stopped-at (haskell-debug-parse-stopped-at string))) + (cond + (maybe-stopped-at + (setq haskell-debug-bindings-cache + maybe-stopped-at) + (message "Computation paused.") + (haskell-debug/refresh)) + (t + (if context + (message "Computation finished.") + (progn + (haskell-mode-toggle-interactive-prompt-state) + (unwind-protect + (when (y-or-n-p "Computation completed without breaking. Reload the module and retry?") + (message "Reloading and resetting breakpoints...") + (haskell-interactive-mode-reset-error (haskell-debug-session)) + (cl-loop for break in breakpoints + do (haskell-process-queue-sync-request + (haskell-debug-process) + (concat ":load " (plist-get break :path)))) + (cl-loop for break in breakpoints + do (haskell-debug-break break)) + (haskell-debug/step expr)) + (haskell-mode-toggle-interactive-prompt-state t)))))))))) + (haskell-debug/refresh))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Internal functions + +(defun haskell-debug-session () + "Get the Haskell session." + (or (haskell-session-maybe) + (error "No Haskell session associated with this debug + buffer. Please just close the buffer and start again."))) + +(defun haskell-debug-process () + "Get the Haskell session." + (or (haskell-session-process (haskell-session-maybe)) + (error "No Haskell session associated with this debug + buffer. Please just close the buffer and start again."))) + +(defun haskell-debug-buffer-name (session) + "The debug buffer name for the current session." + (format "*debug:%s*" + (haskell-session-name session))) + +(defun haskell-debug-get-breakpoints () + "Get the list of breakpoints currently set." + (let ((string (haskell-process-queue-sync-request + (haskell-debug-process) + ":show breaks"))) + (if (string= string "No active breakpoints.\n") + (list) + (mapcar #'haskell-debug-parse-break-point + (haskell-debug-split-string string))))) + +(defun haskell-debug-get-modules () + "Get the list of modules currently set." + (let ((string (haskell-process-queue-sync-request + (haskell-debug-process) + ":show modules"))) + (if (string= string "") + (list) + (mapcar #'haskell-debug-parse-module + (haskell-debug-split-string string))))) + +(defun haskell-debug-get-context () + "Get the current context." + (let ((string (haskell-process-queue-sync-request + (haskell-debug-process) + ":show context"))) + (if (string= string "") + nil + (haskell-debug-parse-context string)))) + +(defun haskell-debug-get-history () + "Get the step history." + (let ((string (haskell-process-queue-sync-request + (haskell-debug-process) + ":history"))) + (if (or (string= string "") + (string= string "Not stopped at a breakpoint\n")) + nil + (if (string= string "Empty history. Perhaps you forgot to use :trace?\n") + nil + (let ((entries (mapcar #'haskell-debug-parse-history-entry + (cl-remove-if (lambda (line) (or (string= "<end of history>" line) + (string= "..." line))) + (haskell-debug-split-string string))))) + (setq haskell-debug-history-cache + entries) + entries))))) + +(defun haskell-debug-insert-bindings (modules breakpoints context) + "Insert a list of bindings." + (if breakpoints + (progn (haskell-debug-insert-binding "t" "trace an expression") + (haskell-debug-insert-binding "s" "step into an expression") + (haskell-debug-insert-binding "b" "breakpoint" t)) + (progn + (when modules + (haskell-debug-insert-binding "b" "breakpoint")) + (when breakpoints + (haskell-debug-insert-binding "s" "step into an expression" t)))) + (when breakpoints + (haskell-debug-insert-binding "d" "delete breakpoint")) + (when context + (haskell-debug-insert-binding "a" "abandon context") + (haskell-debug-insert-binding "c" "continue" t)) + (when context + (haskell-debug-insert-binding "p" "previous step") + (haskell-debug-insert-binding "n" "next step" t)) + (haskell-debug-insert-binding "g" "refresh" t) + (insert "\n")) + +(defun haskell-debug-insert-current-context (context history) + "Insert the current context." + (haskell-debug-insert-header "Context") + (if context + (haskell-debug-insert-context context history) + (haskell-debug-insert-debug-finished)) + (insert "\n")) + +(defun haskell-debug-insert-breakpoints (breakpoints) + "insert the list of breakpoints." + (haskell-debug-insert-header "Breakpoints") + (if (null breakpoints) + (haskell-debug-insert-muted "No active breakpoints.") + (cl-loop for break in breakpoints + do (insert (propertize (format "%d" + (plist-get break :number)) + 'face `((:weight bold)) + 'break break) + (haskell-debug-muted " - ") + (propertize (plist-get break :module) + 'break break + 'break break) + (haskell-debug-muted + (format " (%d:%d)" + (plist-get (plist-get break :span) :start-line) + (plist-get (plist-get break :span) :start-col))) + "\n"))) + (insert "\n")) + +(defun haskell-debug-insert-modules (modules) + "Insert the list of modules." + (haskell-debug-insert-header "Modules") + (if (null modules) + (haskell-debug-insert-muted "No loaded modules.") + (progn (cl-loop for module in modules + do (insert (propertize (plist-get module :module) + 'module module + 'face `((:weight bold))) + (haskell-debug-muted " - ") + (propertize (file-name-nondirectory (plist-get module :path)) + 'module module)) + do (insert "\n"))))) + +(defun haskell-debug-split-string (string) + "Split GHCi's line-based output, stripping the trailing newline." + (split-string string "\n" t)) + +(defun haskell-debug-parse-context (string) + "Parse the context." + (cond + ((string-match "^--> \\(.+\\)\n \\(.+\\)" string) + (let ((name (match-string 1 string)) + (stopped (haskell-debug-parse-stopped-at (match-string 2 string)))) + (list :name name + :path (plist-get stopped :path) + :span (plist-get stopped :span)))))) + +(defun haskell-debug-insert-binding (binding desc &optional end) + "Insert a helpful keybinding." + (insert (propertize binding 'face 'haskell-debug-keybinding-face) + (haskell-debug-muted " - ") + desc + (if end + "\n" + (haskell-debug-muted ", ")))) + +(defun haskell-debug-insert-header (title) + "Insert a header title." + (insert (propertize title + 'face 'haskell-debug-heading-face) + "\n\n")) + +(defun haskell-debug-insert-context (context history) + "Insert the context and history." + (when context + (insert (propertize (plist-get context :name) 'face `((:weight bold))) + (haskell-debug-muted " - ") + (file-name-nondirectory (plist-get context :path)) + (haskell-debug-muted " (stopped)") + "\n")) + (when haskell-debug-bindings-cache + (insert "\n") + (let ((bindings haskell-debug-bindings-cache)) + (insert + (haskell-debug-get-span-string + (plist-get bindings :path) + (plist-get bindings :span))) + (insert "\n\n") + (cl-loop for binding in (plist-get bindings :types) + do (insert (haskell-fontify-as-mode binding 'haskell-mode) + "\n")))) + (let ((history (or history + (list (haskell-debug-make-fake-history context))))) + (when history + (insert "\n") + (haskell-debug-insert-history history)))) + +(defun haskell-debug-insert-debug-finished () + "Insert message that no debugging is happening, but if there is +some old history, then display that." + (if haskell-debug-history-cache + (progn (haskell-debug-insert-muted "Finished debugging.") + (insert "\n") + (haskell-debug-insert-history haskell-debug-history-cache)) + (haskell-debug-insert-muted "Not debugging right now."))) + +(defun haskell-debug-insert-muted (text) + "Insert some muted text." + (insert (haskell-debug-muted text) + "\n")) + +(defun haskell-debug-muted (text) + "Make some muted text." + (propertize text 'face 'haskell-debug-muted-face)) + +(defun haskell-debug-parse-logged (string) + "Parse the logged breakpoint." + (cond + ((string= "no more logged breakpoints\n" string) + nil) + ((string= "already at the beginning of the history\n" string) + nil) + (t + (with-temp-buffer + (insert string) + (goto-char (point-min)) + (list :path (progn (search-forward " at ") + (buffer-substring-no-properties + (point) + (1- (search-forward ":")))) + :span (haskell-debug-parse-span + (buffer-substring-no-properties + (point) + (line-end-position))) + :types (progn (forward-line) + (haskell-debug-split-string + (buffer-substring-no-properties + (point) + (point-max))))))))) + +(defun haskell-debug-parse-stopped-at (string) + "Parse the location stopped at from the given string. + +For example: + +Stopped at /home/foo/project/src/x.hs:6:25-36 + +" + (let ((index (string-match "Stopped at \\([^:]+\\):\\(.+\\)\n?" + string))) + (when index + (list :path (match-string 1 string) + :span (haskell-debug-parse-span (match-string 2 string)) + :types (cdr (haskell-debug-split-string (substring string index))))))) + +(defun haskell-debug-get-span-string (path span) + "Get the string from the PATH and the SPAN." + (save-window-excursion + (find-file path) + (buffer-substring + (save-excursion + (goto-char (point-min)) + (forward-line (1- (plist-get span :start-line))) + (forward-char (1- (plist-get span :start-col))) + (point)) + (save-excursion + (goto-char (point-min)) + (forward-line (1- (plist-get span :end-line))) + (forward-char (plist-get span :end-col)) + (point))))) + +(defun haskell-debug-make-fake-history (context) + "Make a fake history item." + (list :index -1 + :path (plist-get context :path) + :span (plist-get context :span))) + +(defun haskell-debug-insert-history (history) + "Insert tracing HISTORY." + (let ((i (length history))) + (cl-loop for span in history + do (let ((string (haskell-debug-get-span-string + (plist-get span :path) + (plist-get span :span)))) + (insert (propertize (format "%4d" i) + 'face 'haskell-debug-trace-number-face) + " " + (haskell-debug-preview-span + (plist-get span :span) + string + t) + "\n") + (setq i (1- i)))))) + +(defun haskell-debug-parse-span (string) + "Parse a source span from a string. + +Examples: + + (5,1)-(6,37) + 6:25-36 + 5:20 + +People like to make other people's lives interesting by making +variances in source span notation." + (cond + ((string-match "\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\)" + string) + (list :start-line (string-to-number (match-string 1 string)) + :start-col (string-to-number (match-string 2 string)) + :end-line (string-to-number (match-string 1 string)) + :end-col (string-to-number (match-string 3 string)))) + ((string-match "\\([0-9]+\\):\\([0-9]+\\)" + string) + (list :start-line (string-to-number (match-string 1 string)) + :start-col (string-to-number (match-string 2 string)) + :end-line (string-to-number (match-string 1 string)) + :end-col (string-to-number (match-string 2 string)))) + ((string-match "(\\([0-9]+\\),\\([0-9]+\\))-(\\([0-9]+\\),\\([0-9]+\\))" + string) + (list :start-line (string-to-number (match-string 1 string)) + :start-col (string-to-number (match-string 2 string)) + :end-line (string-to-number (match-string 3 string)) + :end-col (string-to-number (match-string 4 string)))) + (t (error "Unable to parse source span from string: %s" + string)))) + +(defun haskell-debug-preview-span (span string &optional collapsed) + "Make a one-line preview of the given expression." + (with-temp-buffer + (haskell-mode) + (insert string) + (when (/= 0 (plist-get span :start-col)) + (indent-rigidly (point-min) + (point-max) + 1)) + (if (fboundp 'font-lock-ensure) + (font-lock-ensure) + (with-no-warnings (font-lock-fontify-buffer))) + (when (/= 0 (plist-get span :start-col)) + (indent-rigidly (point-min) + (point-max) + -1)) + (goto-char (point-min)) + (if collapsed + (replace-regexp-in-string + "\n[ ]*" + (propertize " " 'face 'haskell-debug-newline-face) + (buffer-substring (point-min) + (point-max))) + (buffer-string)))) + +(defun haskell-debug-start (session) + "Start the debug mode." + (setq buffer-read-only t) + (haskell-session-assign session) + (haskell-debug/refresh)) + +(defun haskell-debug () + "Start the debugger for the current Haskell (GHCi) session." + (interactive) + (let ((session (haskell-debug-session))) + (switch-to-buffer-other-window (haskell-debug-buffer-name session)) + (unless (eq major-mode 'haskell-debug-mode) + (haskell-debug-mode) + (haskell-debug-start session)))) + +(defun haskell-debug-break (break) + "Set BREAK breakpoint in module at line/col." + (haskell-process-queue-without-filters + (haskell-debug-process) + (format ":break %s %s %d" + (plist-get break :module) + (plist-get (plist-get break :span) :start-line) + (plist-get (plist-get break :span) :start-col)))) + +(defun haskell-debug-navigate (direction) + "Navigate in DIRECTION \"back\" or \"forward\"." + (let ((string (haskell-process-queue-sync-request + (haskell-debug-process) + (concat ":" direction)))) + (let ((bindings (haskell-debug-parse-logged string))) + (setq haskell-debug-bindings-cache + bindings) + (when (not bindings) + (message "No more %s results!" direction))) + (haskell-debug/refresh))) + +(defun haskell-debug-session-debugging-p (session) + "Does the session have a debugging buffer open?" + (not (not (get-buffer (haskell-debug-buffer-name session))))) + +(defun haskell-debug-highlight (path &optional span) + "Highlight the file at span." + (let ((p (make-overlay + (line-beginning-position) + (line-end-position)))) + (overlay-put p 'face `((:background "#eee"))) + (with-current-buffer + (if span + (save-window-excursion + (find-file path) + (current-buffer)) + (find-file path) + (current-buffer)) + (let ((o (when span + (make-overlay + (save-excursion + (goto-char (point-min)) + (forward-line (1- (plist-get span :start-line))) + (forward-char (1- (plist-get span :start-col))) + (point)) + (save-excursion + (goto-char (point-min)) + (forward-line (1- (plist-get span :end-line))) + (forward-char (plist-get span :end-col)) + (point)))))) + (when o + (overlay-put o 'face `((:background "#eee")))) + (sit-for 0.5) + (when o + (delete-overlay o)) + (delete-overlay p))))) + +(defun haskell-debug-parse-history-entry (string) + "Parse a history entry." + (if (string-match "^\\([-0-9]+\\)[ ]+:[ ]+\\([A-Za-z0-9_':]+\\)[ ]+(\\([^:]+\\):\\(.+?\\))$" + string) + (list :index (string-to-number (match-string 1 string)) + :name (match-string 2 string) + :path (match-string 3 string) + :span (haskell-debug-parse-span (match-string 4 string))) + (error "Unable to parse history entry: %s" string))) + +(defun haskell-debug-parse-module (string) + "Parse a module and path. + +For example: + +X ( /home/foo/X.hs, interpreted ) +Main ( /home/foo/X.hs, /home/foo/X.o ) +" + (if (string-match "\\([^ ]+\\)[ ]+( \\([^ ]+?\\), [/a-zA-Z0-9\.]+ )$" + string) + (list :module (match-string 1 string) + :path (match-string 2 string)) + (error "Unable to parse module from string: %s" + string))) + +(defun haskell-debug-parse-break-point (string) + "Parse a breakpoint number, module and location from a string. + +For example: + +[13] Main /home/foo/src/x.hs:(5,1)-(6,37) + +" + (if (string-match "^\\[\\([0-9]+\\)\\] \\([^ ]+\\) \\([^:]+\\):\\(.+\\)$" + string) + (list :number (string-to-number (match-string 1 string)) + :module (match-string 2 string) + :path (match-string 3 string) + :span (haskell-debug-parse-span (match-string 4 string))) + (error "Unable to parse breakpoint from string: %s" + string))) + +(provide 'haskell-debug) + +;;; haskell-debug.el ends here |