# HG changeset patch # User Jordi GutiƩrrez Hermoso # Date 1531084576 14400 # Node ID 5ff62f07dd477ed6ffd9c3cfe2216f505d6b6171 # Parent 66d1f51b7df364168c49629de73048244f409cfc fountain-mode: update to version 2.5.3 diff --git a/dotemacs.el b/dotemacs.el --- a/dotemacs.el +++ b/dotemacs.el @@ -420,7 +420,7 @@ ("gnu" . "http://elpa.gnu.org/packages/")))) '(package-selected-packages (quote - (imenu-list olivetti fountain-mode markdown-mode magit js2-mode yaml-mode web-mode undo-tree puppet-mode nginx-mode json-mode jade-mode idomenu haml-mode goto-last-change flymake-haml elpy dockerfile-mode))) + (fountain-mode markdown-mode magit js2-mode yaml-mode web-mode undo-tree puppet-mode nginx-mode json-mode jade-mode idomenu haml-mode goto-last-change flymake-haml elpy dockerfile-mode))) '(safe-local-variable-values (quote ((encoding . utf-8) diff --git a/elpa/fountain-mode-2.4.2/fountain-mode-autoloads.el b/elpa/fountain-mode-2.5.3/fountain-mode-autoloads.el rename from elpa/fountain-mode-2.4.2/fountain-mode-autoloads.el rename to elpa/fountain-mode-2.5.3/fountain-mode-autoloads.el --- a/elpa/fountain-mode-2.4.2/fountain-mode-autoloads.el +++ b/elpa/fountain-mode-2.5.3/fountain-mode-autoloads.el @@ -3,8 +3,8 @@ ;;; Code: (add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) -;;;### (autoloads nil "fountain-mode" "fountain-mode.el" (23186 63957 -;;;;;; 555361 643000)) +;;;### (autoloads nil "fountain-mode" "fountain-mode.el" (23362 31369 +;;;;;; 7313 833000)) ;;; Generated autoloads from fountain-mode.el (add-to-list 'auto-mode-alist '("\\.fountain\\'" . fountain-mode)) diff --git a/elpa/fountain-mode-2.4.2/fountain-mode-pkg.el b/elpa/fountain-mode-2.5.3/fountain-mode-pkg.el rename from elpa/fountain-mode-2.4.2/fountain-mode-pkg.el rename to elpa/fountain-mode-2.5.3/fountain-mode-pkg.el --- a/elpa/fountain-mode-2.4.2/fountain-mode-pkg.el +++ b/elpa/fountain-mode-2.5.3/fountain-mode-pkg.el @@ -1,2 +1,2 @@ ;;; -*- no-byte-compile: t -*- -(define-package "fountain-mode" "2.4.2" "Major mode for screenwriting in Fountain markup" '((emacs "24.5")) :commit "e2878da13e7b87a824ebd6c842e9f552369b220c" :url "https://github.com/rnkn/fountain-mode" :keywords '("wp")) +(define-package "fountain-mode" "2.5.3" "Major mode for screenwriting in Fountain markup" '((emacs "24.5"))) diff --git a/elpa/fountain-mode-2.4.2/fountain-mode.el b/elpa/fountain-mode-2.5.3/fountain-mode.el rename from elpa/fountain-mode-2.4.2/fountain-mode.el rename to elpa/fountain-mode-2.5.3/fountain-mode.el --- a/elpa/fountain-mode-2.4.2/fountain-mode.el +++ b/elpa/fountain-mode-2.5.3/fountain-mode.el @@ -4,8 +4,8 @@ ;; Author: Paul Rankin ;; Keywords: wp -;; Package-Version: 2.4.2 -;; Version: 2.4.2 +;; Package-Version: 2.5.3 +;; Version: 2.5.3 ;; Package-Requires: ((emacs "24.5")) ;; URL: https://github.com/rnkn/fountain-mode @@ -124,7 +124,7 @@ ;; Roadmap ;; ------- -;; See [Milestones](https://github.com/rnkn/fountain-mode/milestones). +;; See [Roadmap](https://github.com/rnkn/fountain-mode/projects/2). ;; History ;; ------- @@ -134,13 +134,14 @@ ;; Tips ;; ---- +;; Ethereum address 0x209C60afd8aF6c61ac4Dbe340d81D4f789DF64D3 ;; Bitcoin Cash address 19gUvL8YUzDKr5GyiHpYeF31BfQm87xM9L ;;; Code: (defconst fountain-version - "2.4.2") + "2.5.3") (defun fountain-version () "Return `fountain-mode' version." @@ -282,6 +283,15 @@ (define-obsolete-variable-alias 'fountain-export-buffer-name 'fountain-export-tmp-buffer-name "2.4.0") +(define-obsolete-variable-alias 'fountain-endnotes-buffer-name + 'fountain-endnotes-buffer "2.5.1") + +(make-obsolete-variable 'fountain-endnotes-window-side + 'fountain-endnotes-display-alist "2.5.1") + +(make-obsolete-variable 'fountain-endnotes-window-size + 'fountain-endnotes-display-alist "2.5.1") + ;;; Customization @@ -783,6 +793,11 @@ :link '(info-link "(emacs) Font Lock") :group 'fountain) +(defface fountain + '((t nil)) + "Default base-level face for `fountain-mode' buffers." + :group 'fountain-faces) + (defface fountain-action '((t nil)) "Default face for action." @@ -948,6 +963,9 @@ (setq-local page-delimiter fountain-page-break-regexp) (setq-local outline-level #'fountain-outline-level) (setq-local require-final-newline mode-require-final-newline) + (setq-local completion-cycle-threshold t) ; FIXME: make user option + (setq-local completion-at-point-functions + '(fountain-completion-at-point)) (setq-local font-lock-extra-managed-props '(line-prefix wrap-prefix invisible)) (setq font-lock-multiline 'undecided) @@ -1230,6 +1248,143 @@ (t (looking-at fountain-action-regexp) 'action))) +;;; Auto-completion + +(defvar-local fountain-completion-scene-headings + nil + "List of scene headings in the current buffer.") + +(defvar-local fountain-completion-characters + nil + "List of characters in the current buffer. +Each element is a cons of the character name, a string, and the +character's priority, an integer. + +n.b. The priority value does not equate to the number of lines +the character has.") + +(defun fountain-completion-update-scene-headings (start end) + "Update `fountain-completion-scene-headings' between START and END. + +Added to `jit-lock-functions'." + (goto-char end) + (if (fountain-match-scene-heading) + (forward-line 1) + (fountain-forward-scene 1)) + (setq end (point)) + (goto-char start) + (fountain-forward-scene 0) + (while (< (point) end) + (if (and (not (and (integerp fountain--edit-line) + (= fountain--edit-line (line-number-at-pos)))) + (fountain-match-scene-heading)) + (let ((scene-heading (match-string-no-properties 3))) + (unless (member scene-heading fountain-completion-scene-headings) + (push scene-heading fountain-completion-scene-headings)))) + (fountain-forward-scene 1))) + +(defun fountain-completion-update-characters (start end) + "Update `fountain-completion-characters' between START and END. + +Added to `jit-lock-functions'." + (goto-char end) + (if (fountain-match-scene-heading) + (forward-line 1) + (fountain-forward-scene 1)) + (setq end (point)) + (goto-char start) + (fountain-forward-scene 0) + (while (< (point) end) + (if (and (not (and (integerp fountain--edit-line) + (= fountain--edit-line (line-number-at-pos)))) + (fountain-match-character)) + (let* ((character (match-string-no-properties 4)) + (candidate (assoc-string character fountain-completion-characters)) + (n (cdr candidate))) + (if (not n) + (push (cons character 1) fountain-completion-characters) + (setq fountain-completion-characters + (delete candidate fountain-completion-characters)) + (push (cons character (1+ n)) fountain-completion-characters)))) + (fountain-forward-character 1)) + (setq fountain-completion-characters + (sort fountain-completion-characters #'(lambda (a b) + (< (cdr b) (cdr a)))))) + +(defun fountain-completion-get-characters () + "Return candidates for completing character. + +First, return second-last speaking character, followed by each +previously speaking character within scene. After that, return +characters from `fountain-completion-characters'." + (lambda (string pred action) + (let (candidates) + (save-excursion + (save-restriction + (widen) + (fountain-forward-character 0) + (while (not (or (fountain-match-scene-heading) + (bobp))) + (if (fountain-match-character) + (let ((character (match-string-no-properties 4))) + (unless (member character candidates) + (push (list character) candidates)))) + (fountain-forward-character -1 'scene)))) + (setq candidates (reverse candidates)) + (let ((contd-character (list (car candidates))) + (alt-character (list (car (cdr candidates)))) + (rest-characters (cdr (cdr candidates)))) + (setq candidates (append alt-character contd-character rest-characters))) + (setq candidates (append candidates + fountain-completion-characters)) + (if (eq action 'metadata) + (list 'metadata + (cons 'display-sort-function 'identity) + (cons 'cycle-sort-function 'identity)) + (complete-with-action action candidates string pred))))) + +(defun fountain-completion-at-point () + "Return completion table for entity at point. +Trigger completion with `completion-at-point' (\\[completion-at-point]). + +Always delimits entity from beginning of line to point. If at a +scene heading, return `fountain-scene-heading-candidates'. If +previous line is blank, return result of +`fountain-completion-get-characters'. + +Set `completion-in-region-mode-map' to nil to retain TAB +keybinding. + +Added to `completion-at-point-functions'." + (let (completion-in-region-mode-map jit-lock-mode) + (list (line-beginning-position) + (point) + (completion-table-case-fold + (cond + ((fountain-match-scene-heading) + fountain-completion-scene-headings) + ((fountain-blank-before-p) + (fountain-completion-get-characters))))))) + +(defun fountain-completion-update () + "Create new completion candidates for current buffer. + +Completion candidates are usually updated automatically with +`jit-lock-mode', however this command will add completion +candidates for the entire buffer. + +Add to `fountain-mode-hook' to have full completion upon load." + (interactive) + (setq fountain-completion-scene-headings nil + fountain-completion-characters nil) + (save-excursion + (save-restriction + (widen) + (fountain-completion-update-scene-headings (point-min) (point-max)) + (fountain-completion-update-characters (point-min) (point-max)))) + (message "Completion candidates updated")) + + ;;; Pages (defgroup fountain-pages () @@ -1266,7 +1421,9 @@ "Move point to appropriate place to break a page. This is usually before point, but may be after if only skipping over whitespace." - (skip-chars-forward "\n\r\s\t") + ;; FIXME: rewrite to account for non-exported elements + (if (looking-at "\n[\n\s\t]*\n") + (goto-char (match-end 0))) (let ((element (fountain-get-element))) (cond ;; If we're are a section heading, scene heading or character, we can @@ -1292,7 +1449,7 @@ (skip-chars-forward "\s\t") (if (not (looking-back (sentence-end) (save-excursion - (fountain-forward-character -1) + (fountain-forward-character 0) (point)))) (forward-sentence -1) ;; This may move to character element, or back within dialogue. If @@ -1335,53 +1492,57 @@ To considerably speed up this function, supply EXPORT-ELEMENTS with `fountain-get-export-elements'." - (unless n (setq n 1)) - (while (< 0 n) - ;; Pages don't begin with blank space, so skip over any at point. - (skip-chars-forward "\n\r\s\t") - (forward-line 0) - ;; If we're at a page break, move to its end and skip over whitespace. - (when (fountain-match-page-break) - (goto-char (match-end 0)) - (skip-chars-forward "\n\r\s\t") - (forward-line 0)) - ;; Start counting lines. - (let ((line-count 0)) - ;; Begin the main loop, which only halts if we reach the end of buffer, a - ;; forced page break, or after the maximum lines in a page. - (while (and (< line-count (cdr (assq fountain-export-page-size - fountain-pages-max-lines))) - (not (or (eobp) - (fountain-match-page-break)))) - (cond - ;; If we're at the end of a line (but not also the beginning, i.e. not a - ;; blank line) then move forward a line and increment line-count. - ((and (eolp) (not (bolp))) - (forward-line 1) - (setq line-count (1+ line-count))) - ;; If we're looking at newline, skip over it and any whitespace and - ;; increment line-count. - ((looking-at "\n*\s*\t*\n") ; FIXME: \r ? - (goto-char (match-end 0)) - (setq line-count (1+ line-count))) - ;; We are at an element. Find what kind of element. If it is not included - ;; in export, skip over without incrementing line-count (implement with - ;; block bounds). Get the line width. - (t - (let ((element (fountain-get-element))) - (if (memq element (or export-elements - (fountain-get-export-elements))) - (progn - (fountain-move-to-fill-width element) - (setq line-count (1+ line-count))) - ;; Element is not exported, so skip it without incrementing - ;; line-count. - (end-of-line) - (skip-chars-forward "\n\r\s\t") - (goto-char (line-beginning-position)))))))) - (skip-chars-forward "\n\r\s\t") - (fountain-goto-page-break-point) - (setq n (1- n)))) + (let ((skip-whitespace-fun + (lambda () + (if (looking-at "[\n\s\t]*\n") + (goto-char (match-end 0)))))) + (unless n (setq n 1)) + (while (< 0 n) + ;; Pages don't begin with blank space, so skip over any at point. + (funcall skip-whitespace-fun) + ;; If we're at a page break, move to its end and skip over whitespace. + (when (fountain-match-page-break) + (goto-char (match-end 0)) + (funcall skip-whitespace-fun)) + ;; Start counting lines. + (let ((line-count 0)) + ;; Begin the main loop, which only halts if we reach the end of buffer, + ;; a forced page break, or after the maximum lines in a page. + (while (and (< line-count (cdr (assq fountain-export-page-size + fountain-pages-max-lines))) + (not (eobp)) + (not (fountain-match-page-break))) + (cond + ;; If we're at the end of a line (but not also the beginning, i.e. + ;; not a blank line) then move forward a line and increment + ;; line-count. + ((and (eolp) (not (bolp))) + (forward-line 1) + (setq line-count (1+ line-count))) + ;; If we're looking at newline, skip over it and any whitespace and + ;; increment line-count. + ((looking-at "[\n\s\t]*\n") + (goto-char (match-end 0)) + (setq line-count (1+ line-count))) + ;; We are at an element. Find what kind of element. If it is not + ;; included in export, skip over without incrementing line-count + ;; (implement with block bounds). Get the line width. + (t + (let ((element (fountain-get-element))) + (if (memq element (or export-elements + (fountain-get-export-elements))) + (progn + (fountain-move-to-fill-width element) + (setq line-count (1+ line-count))) + ;; Element is not exported, so skip it without incrementing + ;; line-count. + (end-of-line) + (funcall skip-whitespace-fun))))))) + ;; We are not at the furthest point in a page. Skip over any remaining + ;; whitespace, then go back to page-break point. + (skip-chars-forward "\n\s\t") + (fountain-goto-page-break-point) + (setq n (1- n))))) (defun fountain-move-to-fill-width (element) "Move point to column of ELEMENT fill limit suitable for breaking line. @@ -1401,7 +1562,7 @@ (setq i (1+ i)))))) (skip-chars-forward "\s\t") (if (eolp) (forward-line 1)) - (fill-move-to-break-point (line-beginning-position)))) + (unless (bolp) (fill-move-to-break-point (line-beginning-position))))) (defun fountain-insert-page-break (&optional string) "Insert a page break at appropriate place preceding point. @@ -2172,6 +2333,7 @@ :template fountain-export-tex-template :string-replace (("%" "\\\\%") ("&" "\\\\&") + ("#" "\\\\#") ("\\$" "\\\\$") ("\\*\\*\\*\\(.+?\\)\\*\\*\\*" "\\\\textbf{\\\\emph{\\1}}") ("\\*\\*\\(.+?\\)\\*\\*" "\\\\textbf{\\1}") @@ -3410,11 +3572,7 @@ (defcustom fountain-outline-startup-level 0 - "Outline level to show when visiting a file. - -This can be set on a per-file basis by including in metadata: - -\tstartup-level: N" + "Outline level to show when visiting a file." :type '(choice (const :tag "Show all" 0) (const :tag "Show top-level" 1) (const :tag "Show scene headings" 6) @@ -3650,6 +3808,43 @@ (string-width (match-string 2))) (t 6))) +(defcustom fountain-pop-up-indirect-windows + nil + "Non-nil if opening indirect buffers should make a new window." + :type 'boolean + :group 'fountain) + +(defun fountain-outline-to-indirect-buffer () + "Clone section/scene at point to indirect buffer. + +Set `fountain-pop-up-indirect-windows' to control how indirect +buffer windows are opened." + (interactive) + (let ((pop-up-windows fountain-pop-up-indirect-windows) + (base-buffer (buffer-name (buffer-base-buffer))) + beg end heading-name target-buffer) + (save-excursion + (save-restriction + (widen) + (outline-back-to-heading t) + (setq beg (point)) + (when (or (fountain-match-section-heading) + (fountain-match-scene-heading)) + (setq heading-name (match-string-no-properties 3) + target-buffer (concat base-buffer "-" heading-name)) + (outline-end-of-subtree) + (setq end (point))))) + (if (and (get-buffer target-buffer) + (with-current-buffer target-buffer + (goto-char beg) + (and (or (fountain-match-section-heading) + (fountain-match-scene-heading)) + (string= heading-name (match-string-no-properties 3))))) + (pop-to-buffer target-buffer) + (clone-indirect-buffer target-buffer t) + (outline-show-all)) + (narrow-to-region beg end))) + ;;; Navigation @@ -3797,7 +3992,7 @@ support endnotes." :group 'fountain) -(defcustom fountain-endnotes-buffer-name +(defcustom fountain-endnotes-buffer "%s" "Name of buffer in which to display file endnotes. `%s' is replaced with `buffer-name'. @@ -3806,45 +4001,28 @@ :type 'string :group 'fountain-endnotes) +(defcustom fountain-endnotes-display-alist + '((side . right) + (window-width . 40) + (slot . 1)) + "Alist used to display endnotes buffer. + +See `display-buffer-in-side-window' for example options." + :type 'alist + :group 'fountain-endnotes) + (defcustom fountain-endnotes-select-window nil "If non-nil, switch to endnotes window upon displaying it." :type 'boolean :group 'fountain-endnotes) -(defcustom fountain-endnotes-window-side - 'right - "Preferred side of frame to display endnotes window." - :type '(choice (const :tag "Left" left) - (const :tag "Right" right) - (const :tag "Top" top) - (const :tag "Bottom" bottom)) - :group 'fountain-endnotes) - -(defcustom fountain-endnotes-window-size - '(0.3 0.25) - "Height and width of the endnotes window as a fraction of root window." - :type '(list (float :tag "Height") - (float :tag "Width")) - :group 'fountain-endnotes) - -;; (defcustom fountain-endnotes-display-function -;; 'display-buffer-pop-up-window -;; "Buffer display function used to display endnotes." -;; :type '(radio (const :tag "Pop-up new window" display-buffer-pop-up-window) -;; (const :tag "Pop-up new frame" display-buffer-pop-up-frame) -;; (const :tag "Show in same window" display-buffer-same-window)) -;; :group 'fountain-endnotes) - (defun fountain-show-or-hide-endnotes () "Pop up a window containing endnotes of current buffer. Display a window containing an indirect clone of the current buffer, narrowed to the first endnotes page break to the end of -buffer. - -The window displayed is a special \"side\" window, which will -persist even when calling \\[delete-other-windows]." +buffer." (interactive) (set-buffer (or (buffer-base-buffer) (current-buffer))) (save-excursion @@ -3853,23 +4031,20 @@ (goto-char (point-min)) (let ((beg (if (re-search-forward fountain-end-regexp nil t) (point))) - (src (current-buffer)) - (buf (format fountain-endnotes-buffer-name (buffer-name)))) + (buffer (current-buffer)) + (endnotes-buffer (format fountain-endnotes-buffer (buffer-name)))) (if beg - (if (get-buffer-window buf (selected-frame)) - (delete-windows-on buf (selected-frame)) + (if (get-buffer-window endnotes-buffer (selected-frame)) + (delete-windows-on endnotes-buffer (selected-frame)) (display-buffer-in-side-window - (or (get-buffer buf) - (make-indirect-buffer src buf t)) - (list (cons 'inhibit-same-window t) - (cons 'side fountain-endnotes-window-side) - (cons 'window-height (car fountain-endnotes-window-size)) - (cons 'window-width (cadr fountain-endnotes-window-size)))) - (with-current-buffer buf + (or (get-buffer endnotes-buffer) + (make-indirect-buffer buffer endnotes-buffer t)) + fountain-endnotes-display-alist) + (with-current-buffer endnotes-buffer (narrow-to-region (1+ beg) (point-max))) (if fountain-endnotes-select-window - (select-window (get-buffer-window buf (selected-frame)))) - (message "Showing `%s' endnotes; %s to hide" src + (select-window (get-buffer-window endnotes-buffer (selected-frame)))) + (message "Showing `%s' endnotes; %s to hide" (buffer-name buffer) (key-description (where-is-internal this-command overriding-local-map t)))) (user-error "Buffer `%s' does not contain endnotes" (buffer-name))))))) @@ -3877,6 +4052,19 @@ ;;; Editing +(require 'help) + +(defvar-local fountain--edit-line + nil + "Line number currently being edited. +Prevents incomplete strings added to candidates.") + +(defun fountain-set-edit-line () + "Set `fountain--edit-line' to current line. + +Added to `post-command-hook'." + (setq fountain--edit-line (line-number-at-pos))) + (defcustom fountain-auto-upcase-scene-headings t "If non-nil, automatically upcase lines matching `fountain-scene-heading-regexp'." @@ -3892,6 +4080,22 @@ nil "Overlay used for auto-upcasing current line.") +(defcustom fountain-tab-command + 'fountain-dwim + "Command to call when pressing the TAB key." + :type '(radio (function-item fountain-dwim) + (function-item fountain-outline-cycle) + (function-item fountain-toggle-auto-upcase) + (function-item completion-at-point)) + :group 'fountain) + +(defun fountain-tab-action (&optional arg) + "Simply calls the value of variable `fountain-tab-command'." + (interactive "p") + (if (help-function-arglist fountain-tab-command) + (funcall fountain-tab-command arg) + (funcall fountain-tab-command))) + (defun fountain-auto-upcase-make-overlay () "Make the auto-upcase overlay on current line. @@ -3914,12 +4118,27 @@ Added as hook to `post-command-hook'." (when (or deactivate (and (integerp fountain--auto-upcase-line) - (/= fountain--auto-upcase-line - (count-lines (point-min) (line-beginning-position))))) + (/= fountain--auto-upcase-line (line-number-at-pos)))) (setq fountain--auto-upcase-line nil) (if (overlayp fountain--auto-upcase-overlay) (delete-overlay fountain--auto-upcase-overlay)) - (message "Auto-upcasing disabled"))) + (message "Auto-upcasing deactivated"))) + +(defun fountain-toggle-auto-upcase () + "Toggle line auto-upcasing. + +Upcase the current line, and continue to upcase inserted +characters until either disabled, or point moves to a different +line (by inserting a newline or by point motion). + +The auto-upcased line is highlighted with face +`fountain-auto-upcase-highlight'" + (interactive) + (if fountain--auto-upcase-line + (fountain-auto-upcase-deactivate-maybe t) + (setq fountain--auto-upcase-line (line-number-at-pos)) + (message "Auto-upcasing activated") + (fountain-auto-upcase))) (defun fountain-auto-upcase () "Upcase all or part of the current line contextually. @@ -3933,63 +4152,44 @@ Added as hook to `post-self-insert-hook'." (cond ((and fountain-auto-upcase-scene-headings (fountain-match-scene-heading)) - (setq fountain--auto-upcase-line - (count-lines (point-min) (line-beginning-position))) + (unless (and (integerp fountain--auto-upcase-line) + (= fountain--auto-upcase-line (line-number-at-pos))) + (setq fountain--auto-upcase-line (line-number-at-pos)) + (message "Auto-upcasing activated")) (fountain-auto-upcase-make-overlay) - (upcase-region (line-beginning-position) - (or (match-end 3) - (point)))) + (upcase-region (line-beginning-position) (or (match-end 3) (point)))) ((and (integerp fountain--auto-upcase-line) - (= fountain--auto-upcase-line - (count-lines (point-min) (line-beginning-position)))) + (= fountain--auto-upcase-line (line-number-at-pos))) + (fountain-auto-upcase-make-overlay) (fountain-upcase-line)))) (defun fountain-dwim (&optional arg) "\\Call a command based on context (Do What I Mean). 1. If point is at a scene heading or section heading, or if - prefixed with ARG (\\[universal-argument] \\[fountain-dwim]) call `fountain-outline-cycle' - and pass ARG, e.g. \\[universal-argument] \\[universal-argument] \\[fountain-dwim] is the same as - \\[universal-argument] \\[universal-argument] \\[fountain-outline-cycle]. + prefixed with ARG call `fountain-outline-cycle' and pass ARG. 2. If point is at an directive to an included file, call `fountain-include-find-file'. -3. Otherwise, upcase the current line and active auto-upcasing. - This highlights the current line with face - `fountain-auto-upcase-highlight' and will continue to upcase - inserted characters until the command is called again - (\\[fountain-dwim]) or point moves to a different line (either - by inserting a newline or point motion). This allows a - flexible style of entering character names. You may press - \\[fountain-dwim] before, during or after typing the name to - get the same result." +3. Otherwise, call `fountain-toggle-auto-upcase'." (interactive "p") - (cond ((< 1 arg) + (cond ((and arg (< 1 arg)) (fountain-outline-cycle arg)) ((or (fountain-match-section-heading) (fountain-match-scene-heading)) (fountain-outline-cycle)) ((fountain-match-include) (fountain-include-find-file)) - (fountain--auto-upcase-line - (fountain-auto-upcase-deactivate-maybe t)) (t - (setq fountain--auto-upcase-line - (count-lines (point-min) (line-beginning-position))) - (fountain-auto-upcase-make-overlay) - (fountain-upcase-line) - (message "Auto-upcasing enabled")))) + (fountain-toggle-auto-upcase)))) (defun fountain-upcase-line (&optional arg) "Upcase the line. If prefixed with ARG, insert `.' at beginning of line to force a scene heading." (interactive "P") - (if arg - (save-excursion - (forward-line 0) - (insert "."))) + (if arg (save-excursion (forward-line 0) (insert "."))) (upcase-region (line-beginning-position) (line-end-position))) (defun fountain-upcase-line-and-newline (&optional arg) @@ -4017,26 +4217,6 @@ (delete-region x (point)) (unless (eobp) (forward-char 1))))))) -(defun fountain-insert-alternate-character () - "Insert second-last character within the scene, and newline." - (interactive) - (let* ((n -1) - (character-1 (fountain-get-character n 'scene)) - (character-2 character-1)) - (while (and (stringp character-1) - (string= character-1 character-2)) - (setq n (1- n) - character-2 (fountain-get-character n 'scene))) - (if character-2 - (let ((x (save-excursion - (skip-chars-backward "\s\n\t") - (point)))) - (delete-region x (point)) - (newline 2) - (insert character-2)) - (message "No alternate character within scene")) - (newline))) - (defun fountain-insert-synopsis () "Insert synopsis below scene heading of current scene." (interactive) @@ -4689,7 +4869,7 @@ (defvar fountain-mode-map (let ((map (make-sparse-keymap))) ;; Editing commands: - (define-key map (kbd "TAB") #'fountain-dwim) + (define-key map (kbd "TAB") #'fountain-tab-action) (define-key map (kbd "C-c RET") #'fountain-upcase-line-and-newline) (define-key map (kbd "") #'fountain-upcase-line-and-newline) (define-key map (kbd "C-c C-c") #'fountain-upcase-line) @@ -4701,6 +4881,8 @@ (define-key map (kbd "C-c C-x _") #'fountain-remove-scene-numbers) (define-key map (kbd "C-c C-x f") #'fountain-set-font-lock-decoration) (define-key map (kbd "C-c C-x RET") #'fountain-insert-page-break) + (define-key map (kbd "M-TAB") #'completion-at-point) + (define-key map (kbd "C-c C-x a") #'fountain-completion-update) ;; FIXME: include-find-file feels like it should be C-c C-c... ;; (define-key map (kbd "C-c C-c") #'fountain-include-find-file) ;; Navigation commands: @@ -4725,6 +4907,7 @@ (define-key map (kbd "C-c TAB") #'fountain-outline-cycle) (define-key map (kbd "") #'fountain-outline-cycle-global) (define-key map (kbd "S-TAB") #'fountain-outline-cycle-global) + (define-key map (kbd "C-c C-x b") #'fountain-outline-to-indirect-buffer) ;; Pages (define-key map (kbd "C-c C-x p") #'fountain-count-pages) ;; Endnotes: @@ -4762,6 +4945,8 @@ ["Cycle Scene/Section Visibility" fountain-outline-cycle] ["Cycle Global Visibility" fountain-outline-cycle-global] "---" + ["Open Scene/Section in Indirect Buffer" fountain-outline-to-indirect-buffer] + "---" ["Up Heading" fountain-outline-up] ["Next Heading" fountain-outline-next] ["Previous Heading" fountain-outline-previous] @@ -4787,11 +4972,11 @@ (customize-set-variable 'fountain-pages-show-in-mode-line nil) :style radio :selected (not fountain-pages-show-in-mode-line)] - ["In Mode Line with Manual Update" + ["Show in Mode Line with Manual Update" (customize-set-variable 'fountain-pages-show-in-mode-line 'force) :style radio :selected (eq fountain-pages-show-in-mode-line 'force)] - ["In Mode Line with Automatic Update" + ["Show in Mode Line with Automatic Update" (customize-set-variable 'fountain-pages-show-in-mode-line 'timer) :style radio :selected (eq fountain-pages-show-in-mode-line 'timer)]) @@ -4801,19 +4986,20 @@ ["Insert Note" fountain-insert-note] ["Insert Page Break..." fountain-insert-page-break] ["Refresh Continued Dialog" fountain-continued-dialog-refresh] + ["Update Auto-Completion" fountain-completion-update] "---" ("Show/Hide" ["Endnotes" fountain-show-or-hide-endnotes] - ["Emphasis Delimiters" + ["Hide Emphasis Delimiters" (customize-set-variable 'fountain-hide-emphasis-delim (not fountain-hide-emphasis-delim)) :style toggle - :selected (not fountain-hide-emphasis-delim)] - ["Syntax Characters" + :selected fountain-hide-emphasis-delim] + ["Hide Syntax Characters" (customize-set-variable 'fountain-hide-syntax-chars (not fountain-hide-syntax-chars)) :style toggle - :selected (not fountain-hide-syntax-chars)]) + :selected fountain-hide-syntax-chars]) ("Syntax Highlighting" ["Minimum" (fountain-set-font-lock-decoration 1) @@ -4880,6 +5066,19 @@ ["Customize Export" (customize-group 'fountain-export)]) "---" + ("TAB Command" + ["Contextual (Do What I Mean)" (customize-set-variable 'fountain-tab-command 'fountain-dwim) + :style radio + :selected (eq fountain-tab-command 'fountain-dwim)] + ["Cycle Scene/Section Visibility" (customize-set-variable 'fountain-tab-command 'fountain-outline-cycle) + :style radio + :selected (eq fountain-tab-command 'fountain-outline-cycle)] + ["Toggle Auto-Upcasing" (customize-set-variable 'fountain-tab-command 'fountain-toggle-auto-upcase) + :style radio + :selected (eq fountain-tab-command 'fountain-toggle-auto-upcase)] + ["Auto-Completion" (customize-set-variable 'fountain-tab-command 'completion-at-point) + :style radio + :selected (eq fountain-tab-command 'completion-at-point)]) ["Display Elements Auto-Aligned" (customize-set-variable 'fountain-align-elements (not fountain-align-elements)) @@ -4920,15 +5119,12 @@ (if unsaved (custom-save-all)))) -;;; Syntax Table +;;; Mode Definition (defvar fountain-mode-syntax-table (make-syntax-table) "Syntax table for `fountain-mode'.") - -;;; Mode Definition - ;;;###autoload (add-to-list 'auto-mode-alist '("\\.fountain\\'" . fountain-mode)) @@ -4937,16 +5133,15 @@ "Major mode for screenwriting in Fountain markup." :group 'fountain (fountain-init-vars) - (let ((n (plist-get (fountain-read-metadata) 'startup-level))) - (if (stringp n) - (setq-local fountain-outline-startup-level - (min (string-to-number n) 6)))) - (add-hook 'post-self-insert-hook - #'fountain-auto-upcase nil t) - (add-hook 'post-command-hook - #'fountain-auto-upcase-deactivate-maybe nil t) + (hack-local-variables) + (face-remap-add-relative 'default 'fountain) + (add-hook 'post-command-hook #'fountain-set-edit-line nil t) + (add-hook 'post-command-hook #'fountain-auto-upcase-deactivate-maybe nil t) + (add-hook 'post-self-insert-hook #'fountain-auto-upcase nil t) (if fountain-patch-emacs-bugs (fountain-patch-emacs-bugs)) - (jit-lock-register #'fountain-redisplay-scene-numbers t) + (jit-lock-register #'fountain-redisplay-scene-numbers) + (jit-lock-register #'fountain-completion-update-scene-headings) + (jit-lock-register #'fountain-completion-update-characters) (fountain-init-mode-line) (fountain-restart-page-count-timer) (fountain-outline-hide-level fountain-outline-startup-level t))