 ;; license that can be found in the LICENSE file.
 
 (require 'cl)
-(require 'diff-mode)
 (require 'ffap)
 (require 'url)
 

;;;###autoload
(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))

(defun gofmt ()
  "Pipe the current buffer through the external tool `gofmt`.
Replace the current buffer on success; display errors on failure."

  (interactive)
  (let ((currconf (current-window-configuration)))
    (let ((srcbuf (current-buffer))
          (filename buffer-file-name)
          (patchbuf (get-buffer-create "*Gofmt patch*")))
      (with-current-buffer patchbuf
        (let ((errbuf (get-buffer-create "*Gofmt Errors*"))
              ;; use utf-8 with subprocesses
              (coding-system-for-read 'utf-8)
              (coding-system-for-write 'utf-8))
          (with-current-buffer errbuf
            (setq buffer-read-only nil)
            (erase-buffer))
          (with-current-buffer srcbuf
            (save-restriction
              (let (deactivate-mark)
                (widen)
                ;; If this is a new file, diff-mode can't apply a
                ;; patch to a non-exisiting file, so replace the buffer
                ;; completely with the output of 'gofmt'.
                ;; If the file exists, patch it to keep the 'undo' list happy.
                (let* ((newfile (not (file-exists-p filename)))
                       (flag (if newfile "" " -d")))

                  ;; diff-mode doesn't work too well with missing
                  ;; end-of-file newline, so add one
                  (if (/= (char-after (1- (point-max))) ?\n)
                      (save-excursion
                        (goto-char (point-max))
                        (insert ?\n)))

                  (if (zerop (shell-command-on-region (point-min) (point-max)
                                                      (concat "gofmt" flag)
                                                      patchbuf nil errbuf))
                      ;; gofmt succeeded: replace buffer or apply patch hunks.
                      (let ((old-point (point))
                            (old-mark (mark t)))
                        (kill-buffer errbuf)
                        (if newfile
                            ;; New file, replace it (diff-mode won't work)
                            (gofmt--replace-buffer srcbuf patchbuf)
                          ;; Existing file, patch it
                          (gofmt--apply-patch filename srcbuf patchbuf))
                        (goto-char (min old-point (point-max)))
                        ;; Restore the mark and point
                        (if old-mark (push-mark (min old-mark (point-max)) t))
                        (set-window-configuration currconf))

                    ;; gofmt failed: display the errors
                    (message "Could not apply gofmt. Check errors for details")
                    (gofmt--process-errors filename errbuf))))))

          ;; Collapse any window opened on outbuf if shell-command-on-region
          ;; displayed it.
          (delete-windows-on patchbuf)))
      (kill-buffer patchbuf))))

(defun gofmt--replace-buffer (srcbuf patchbuf)
  (with-current-buffer srcbuf
    (erase-buffer)
    (insert-buffer-substring patchbuf))
  (message "Applied gofmt"))

(defun gofmt--apply-patch (filename srcbuf patchbuf)
  ;; apply all the patch hunks
  (let (changed)
    (with-current-buffer patchbuf
      (goto-char (point-min))
      ;; The .* is for TMPDIR, but to avoid dealing with TMPDIR
      ;; having a trailing / or not, it's easier to just search for .*
      ;; especially as we're only replacing the first instance.
      (if (re-search-forward "^--- \\(.*/gofmt[0-9]*\\)" nil t)
          (replace-match filename nil nil nil 1))
      (condition-case nil
          (while t
            (diff-hunk-next)
            (diff-apply-hunk)
            (setq changed t))
        ;; When there's no more hunks, diff-hunk-next signals an error, ignore it
        (error nil)))
    (if changed (message "Applied gofmt") (message "Buffer was already gofmted"))))

(defun gofmt--process-errors (filename errbuf)
  ;; Convert the gofmt stderr to something understood by the compilation mode.
  (with-current-buffer errbuf
    (goto-char (point-min))
    (insert "gofmt errors:\n")
    (if (search-forward gofmt-stdin-tag nil t)
        (replace-match (file-name-nondirectory filename) nil t))
    (display-buffer errbuf)
    (compilation-mode)))


    (concat "\\_<" s "\\_>")))

(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]")
(defconst go-identifier-regexp "[[:word:][:multibyte:]]+")
(defconst go-label-regexp go-identifier-regexp)
(defconst go-type-regexp "[[:word:][:multibyte:]*]+")
