-
-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
{backward forward}-match
differences, and, at-exp indenter needs get-character
and find-up-sexp
methods
#7
Comments
This won't work 100% as-is with (require racket/gui/base
framework)
(define (insert t str)
(define lp (send t last-position))
(send t insert str lp lp)
(send t freeze-colorer)
(send t thaw-colorer)
(send t freeze-colorer))
(let ()
(define str "#lang at-exp racket\n(1) #(2) #hash((1 . 2))\n@racket[]{\n#(2)\n}\n")
(define o (test-create str))
(define t (new racket:text%))
(send t start-colorer symbol->string default-lexer default-paren-matches)
(insert t str)
(check-equal? (send o last-position)
(send t last-position))
;; Test that our implementations of {forward backward}-match are
;; equivalent to those of racket:text%.
(define lp (string-length str))
;; FIXME: These tests currently mostly fail. I'm not yet sure if
;; that's because mflatt did a subset of behavior needed by
;; indenters, or due to some other problem.
#;
(for ([pos (in-range 0 (string-length str))])
(send t set-position pos pos)
(check-equal? (send o forward-match pos lp)
(send t forward-match pos lp)
(format "forward-match ~v" pos))
(check-equal? (send o backward-match pos lp)
(send t backward-match pos lp)
(format "backward-match ~v" pos)))
;; Test that we supply enough entire color-text% methods, and that
;; they behave equivalently to from racket-text%, as needed by a
;; lang-supplied drracket:indentation a.k.a. determine-spaces
;; function. (After all, this is the motivation to provide
;; text%-like methods; otherwise we wouldn't bother.)
;; FIXME: These errored until I added `get-char` and `find-up-sexp`
;; methods. Now they merely fail because my `find-up-sexp` is a
;; dummy placeholder that's ~= add1.
(define determine-spaces (send o -get-line-indenter))
(for ([pos (in-range 0 (string-length str))])
(check-equal? (determine-spaces o pos)
(determine-spaces t pos)
(format "~v ~v" determine-spaces pos)))) |
I think we made a decision earlier but I wasn't sure so apologies if this message is adam-and-eveish. It seems like there is a fork in the road: we can either 1) try to detangle color:text from the rest of the GUI libraries because we like it and then reuse it here, 2) we can abandon it and use Greg's coloring code instead, or 3) we can try to keep the two implementations because they have different properties (and then build test suites to help ensure the top-level operations) always do the same things. From the earlier conversation, my impression was that Greg's code was faster overall for the coloring part but it wasn't incremental so the very slow cases were too slow to be blocking the eventspace handling thread. Does this mean that we should be taking option 3 in the previous paragraph? There are definitely different setups here because, in the Emacs world, there is already a required async call between the keystroke in Emacs and the racket code that computes based on it, but that isn't required in the DrRacket world. Maybe that's enough of a game changer that option 3 is the only one that makes sense? Thanks. |
To the extent I might be "losing the plot", I apologize. I'm a little overwhelmed at the moment from
|
Not at all! My thinking now is that we need to keep the two implementations (so, option 3) but I just wanted to make sure you were in agreement. Absent other specifics, I see option 3 as the worst option (maintaining two implementations) and I really like what you're doing which pushes me towards option 2, but my sense is that the use cases are different enough and the performance characteristics that make sense are different enough that we're going to, at least for now, stick with option 3. Does that make sense to you? |
As warmup, a report of latest on the Racket Mode side:
I don't know if my recent changes I described above, make you any more inclined to do option 2? Certainly I'm not pitching/selling/urging you. For Dr Racket it might be fine to make no change, or at most to use the idea of stopping early for merely-shifted tokens. Anyway I think option 2 is really about coloring. I think the crux of the challenge is indentation functions needing Option 1 is of course the ideal, "correct" thing to do. In a world where "correct" is work done by someone else and causes no bugs to fix. 😄 Seriously, it does seem ideal, to me, but you would probably be doing most of that work, not me, so I feel like my opinion isn't the important one. I think option 3 might be OK? It wouldn't be awful, today, to do some copypasta. I'm slightly nervous the number of methods will keep growing over time, maybe to where option 1 will look good in hindsight? But I don't know. |
The For things like |
@greghendershott thank you for the thoughful analysis. Without too much quoting, here are some responses
I think this might be true if the performance of option 2 was similar or worse but my experiments suggest that option 2's performance was better. I am not confident in that, however, and would be interested in working together to try to get a better handle on the relative performance.
The framework's colorer has a similar capability. Internally, it knows a region of the buffer that's wrong -- the back end of this region moves forward as the colorer reports tokens until it shortcircuits (as your code does) or reaches the end. When someone does an indent (or need to know where a matching delimiter is), the event handling thread just until the colorer has progressed far enough that it has the answer (it could also do a check and beep instead that wouldn't block and I vaguely recall this option is used somewhere, but it isn't used for indent nor is it used for the editor navigation keystrokes (eg forward-sexp); those block instead). As for |
I think that's one difference. I took a crack at defining (define/public (skip-whitespace pos direction comments?)
(let loop ([pos pos])
(cond [(and (eq? direction 'forward)
(>= pos (last-position)))
pos]
[(and (eq? direction 'backward)
(<= pos 0))
pos]
[else
(define token-pos (if (eq? direction 'forward) pos (sub1 pos)))
(define-values (s e) (get-token-range token-pos))
(define type (classify-position token-pos))
(cond [(or (eq? type 'white-space)
(and comments? (eq? type 'comment)))
(loop (if (eq? direction 'forward)
e
s))]
[else pos])]))) Another probable difference: My versions of So hopefully that's a tiny head start when you have a chance to look at this in >= a few days. Even with those changes, where my I think the problem here is that they're documented to stop at non-parens, too:
Although I don't necessarily understand the rationale for that, it's the current behavior, and I don't know if any indenters depend on it, so it probably needs to be preserved? Here when I cheat and look at the implementation of [[Whereas I'd prefer (a) to have some outer guard that checks whether it's OK to proceed, and then these individual methods can be simpler, and (b) in my experiments I found iterating tokens is fast enough (even in a big old Again, I know you can't work on this for at least a few days, but I wanted to share this in case it was helpful later. |
re the rationale for backward and forward match and non-parens: I think this has to do with the alt-movement keys moving over atomic sexpressions. So in a situation where someone is trying to move forward over an expression and that expression is, say, a string, then we get the expected behavior. Here's some calls that try to demonstrate that:
the call at the end gets us to the end of the buffer (which is the end of the string). |
Here's my attempt to explain what (define/private (do-forward-match position cutoff skip-whitespace?)
(let ((position
(if skip-whitespace?
(skip-whitespace position 'forward #t)
position)))
;; skip the whitespace
(let ([ls (find-ls position)])
;; the editor might have multiple regions that are colored separately
;; (think: interactions window with each repl input section being colorerd separately
;; and the regions between them (where io and results go) not being colored)
;; this gets the right one.
(and
ls
(let-values (((start end error)
(send (lexer-state-parens ls) match-forward
(- position (lexer-state-start-pos ls)))))
;; this asks for the matching position of the paren at position (the subtraction is
;; to deal with these regions the coordinates inside the `ls` are relative to the ls's region)
;; the match-forward method is the one in paren-tree.rkt.
(cond
((f-match-false-error ls start end error)
;; this is part of the incremental stuff -- it means that we need to ask the code that's
;; interacting with the tokenizer to go get some more tokesn
(colorer-driver)
;; which is what colorer-driver does
(do-forward-match position cutoff #f))
;; and then we try again
((and start end (not error))
;; here we found a match for the paren, so return it, after coordinate transformations
(let ((match-pos (+ (lexer-state-start-pos ls) end)))
(cond
((<= match-pos cutoff) match-pos)
(else #f))))
((and start end error) #f)
;; dunno what error is about; this whole thing may be dead
(else
(skip-past-token ls position))))))))
;; and here we didn't find a parenthesis so we give the other end of the token (ala my previous comment) |
@rfindler Thanks for the additional explanation!
✔️ What do to in an interactions/REPL buffer is something I'd been deferring, while trying to work out how to use all these |
@mflatt I'm not sure if I'm looking at the right repo, is this the latest rhombus implementation, for now, supplying only Asking only because if there were something already supplying e.g. But if not yet (or if it's not quite working due to e.g. this issue I'm commenting on) no worries. I can set it aside; plenty else to work on meanwhile. |
The support in the framework's colorer for the REPL comes in the form of the reset-regions method. Lifting this restriction about editing outside the regions is probably not too difficult (especially given data/interval-map, which didn't exist back when this code was written) |
@greghendershott That Here's the current version of the shrubbery It points to this implementation of indentation support: https://github.com/mflatt/shrubbery-rhombus-0/blob/master/shrubbery/indentation.rkt |
I've made Tests based on @greghendershott's example are in a new A next step, still, is to extract the default Racket indentation and navigation functions from |
@greghendershott Do you have any opinion on where the extracted Racket default indentation and navigation functions should go? I've started putting them in |
@mflatt This morning I'm catching up with your changes. Everything looks good so far, with one small exception: When I add a test exercising That is, This after I fetched NOTE: I'm doing an equivalent test in my "like-text fork". If you add to |
@mflatt I'm not sure the best home, and I probably need to finish up a couple things before mentally switching gears to think about packaging and delivery. (A quick reaction: |
@greghendershott Thanks for the report on the mismatch with the shrubbery lexer. The problem there was exposed by a bug in the shrubbery lexer, where the lexer reports a non-parenthesis symbol as a parenthesis. Of course, |
Background: I have a variant copy (for the time being) of
like-text%
fromobject.rkt
. It uses my tokenizer strategy that can stop early on "convergence". As we discussed if it seems suitable for expeditor I can contribute that back here.Looking at
{backward forward}-match
docs and code more carefully, I felt less confident I understand what they're supposed to do. The official docs are a bit terse. (On racket/drracket#527 I think part of my confusion was mistaking these for something like{backward forward}-sexp
.)As a sanity check I wrote a test to create my
like-text%
object, also create aracket:text%
object and callstart-colorer
. Then compare callingforward-match
andbackward-match
on both methods. Right away I'm seeing they're not always equivalent.Is this a bug, or, maybe as-intended?
Was maybe your idea was that, not only can a like-text% be some documented subset of all
color-text<%>
methods, for indenters, maybe it's also OK if some methods differ in behavior, if it doesn't matter for an indenter??So next I added a test for that: Compare giving both objects to an indentation function. The only non-#f-returning indentation function I know about is the scribble/at-exp one. So I used that. Right away, I discovered it errors wanting two more methods available:
get-character
. This is trivial.find-up-sexp
. Theracket:text%
implementation offind-up-sexp
seems to use a fair number of other methods defined there, as well as inherited (likebackward-containing-sexp
).Does it make sense to start copying equivalents of those for a
like-text%
, too??Or is that too much, some of it should be split off into its own interface/class that can be shared among
racket:text%
and "like-text%" objects in expeditor or other users??I don't have a crisp picture of how all the status quo pieces fit together, and I'm super weak on
racket/class
, so unfortunately this is a question not a suggestion.The text was updated successfully, but these errors were encountered: