parinfer / parinfer.js

Let's simplify the way we write Lisp
https://shaunlebron.github.io/parinfer
MIT License
1.76k stars 40 forks source link

Tab-stops usage clarification #130

Closed Kurvivor19 closed 7 years ago

Kurvivor19 commented 7 years ago

How can one use tab stops provided by parinferlib? What is the intent? As far as i can tell, tab-stops are really only intended for use in indent mode, as changing indent by tabbng goes against the purpose of paren mode

So, in indent mode, how can tab stops be used to: a) determine the context - string literals, S-expressions and comments should be probably indented differently. Probably string literals should insert literal tabs? And comments starting with ;;; should be on the first column? b) determine actual positions for tab stops. Does tab-stops structure contains only necessary ones or extra ones? If there are several lines, each of which contains several s-forms in them, will not their x-positions be interleaved?

I could not find that info in available documentation. If possible, clarification with examples would be nice.

shaunlebron commented 7 years ago

I have example test cases here: https://github.com/shaunlebron/parinfer/blob/master/lib/test/cases/indent-mode.md#tab-stops

To maybe clarify, here's another illustration of it below. Parinferlib is only giving you the open-paren positions that will be affected by indentation changes of your current line (see the carets ^ below). The idea is that you could use them to insert extra tabstops depending on your one-space or two-space indentation preferences (see the + below). Then, the editor can use this combination of tabstops to jump the cursor between them at the active line (see the ; line below)

(let [a {:foo 1}
| |  ||
^ +  ^+
      bar [1 2 3]]
          ||
          ^+
; tab stops above listed as if the cursor were on THIS line
  bar)

With that, some specific things you asked about:

shaunlebron commented 7 years ago

Tab-stops are generated for a certain cursor position, are they not? if that is the case, they need to be recalculated when cursor moves, is that correct?

@Kurvivor19 tab-stops are per-line, so they will only be recalculate if your cursor changes line

Kurvivor19 commented 7 years ago

I see. So, here is a function for emacs that makes use of them

(defun parinfer-mode--cycle-indent ()
  "Indent the line if in indent mode"
  (interactive)
  (when (and parinfer-mode--tab-stops                     ; that variable is set to what tab stops last call to parinfer library return
             (eql parinfer-mode--current-mode :indent))
    (let* ((old-point (current-column))
           (old-indent (current-indentation))
           (stops parinfer-mode--tab-stops)
           (cur-stop (car stops))
           (target-indent
             (if (>= (plist-get cur-stop :x) old-indent)
                 ;; if we are on the beginning on the line, indent to the most right tab stop
                 (plist-get (car (last stops)) :x)
               (cl-dolist (next-stop stops (plist-get cur-stop :x))
                 ;; find closest tab-stop to the left for the current position
                 (if (< (plist-get next-stop :x) old-indent)
                     (setq cur-stop next-stop)
                   (return (plist-get cur-stop :x)))))))
      (indent-line-to target-indent) ; this places cursor at the end of the indentation; this is acceptable if cursor previously was on the indentation
      (if (> old-point old-indent)
          ;; ensure cursor remains on the same character as before otherwise
          (forward-char (- old-point old-indent))))))