Skip to content

Instantly share code, notes, and snippets.

@Metaxal
Last active October 9, 2022 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Metaxal/5860b23973b99aac247ed998136a0fd4 to your computer and use it in GitHub Desktop.
Save Metaxal/5860b23973b99aac247ed998136a0fd4 to your computer and use it in GitHub Desktop.
A quickscript to select a rectangle of text, like Geany
#lang racket/base
(require quickscript
racket/class
racket/list
racket/gui/base
racket/string
framework)
;;; Author: Laurent Orseau - laurent.orseau at gmail.com
;;; License: [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) or
;;; [MIT license](http://opensource.org/licenses/MIT) at your option.
(script-help-string "Rectangle selection, cut, copy, paste, delete")
#| How it works:
1. Using the normal selection tool, select a top-left to bottom-right "rectangle"
(even though more text is selected than this rectangle)
2. Call one of the Rectangle [Delete, Deselect, Copy, Cut] scripts.
3. Optionally, use normal paste.
|#
(define hkey 'rect-sel)
;; Returns a replacement string for the selected string `selection`
;; ("" if no text is selected), or `#f` to leave the selection as is.
(define-script rectangle-selection
#:label "Rectangle &Select"
#:menu-path ("Sele&ction" "&Rectangle")
(λ (selection #:editor ed)
(define start (send ed get-start-position))
(define start-line (send ed position-line start))
(define start-line-pos (send ed line-start-position start-line))
(define start-col (- start start-line-pos))
(define end (send ed get-end-position))
(define end-line (send ed position-line end))
(define end-line-pos (send ed line-start-position end-line))
(define end-col (- end end-line-pos))
(define top-line (min start-line end-line))
(define bot-line (max start-line end-line))
(define left-col (min start-col end-col))
(define right-col (max start-col end-col))
;; Deselect drracket's selection
(send ed set-position start)
;; Deselect any existing rectangle selection
(rectangle-deselect #f #:editor ed)
;; Highlight rectangle
(send ed begin-edit-sequence)
(for ([line (in-range top-line (+ 1 bot-line))])
(define line-pos (send ed line-start-position line))
(define line-end-pos (send ed line-end-position line))
(when (< (+ line-pos left-col) line-end-pos)
(send ed highlight-range
#:key hkey
(+ line-pos left-col) (min line-end-pos (+ line-pos right-col)) "yellow")))
(send ed end-edit-sequence)
#f))
(define-script rectangle-deselect
#:label "Rectangle D&eselect"
#:menu-path ("Sele&ction" "&Rectangle")
(λ (selection #:editor ed)
(send ed unhighlight-ranges/key hkey)
#f))
(define (get-starts+ends ed [<? <])
;; text:range-key is not exported, so we use a hack to obtain only our ranges.
(define ranges (filter (λ (rg) (eq? (vector-ref (struct->vector rg) 7)
hkey))
(send ed get-highlighted-ranges)))
(for/list ([rg (in-list (sort ranges <? #:key text:range-start))])
(list (text:range-start rg) (text:range-end rg))))
(define-script rectangle-delete
#:label "Rectangle &Delete"
#:menu-path ("Sele&ction" "&Rectangle")
(λ (selection #:editor ed)
(define ranges (get-starts+ends ed >))
(when (empty? ranges)
(rectangle-selection selection #:editor ed)
(set! ranges (get-starts+ends ed >))) ; start with the bottom to avoid shift
(send ed begin-edit-sequence)
(for ([rg (in-list ranges)])
(send ed delete (first rg) (second rg)))
(rectangle-deselect #f #:editor ed)
(send ed end-edit-sequence)
#f))
(define-script rectangle-cut
#:label "Rectangle C&ut"
#:menu-path ("Sele&ction" "&Rectangle")
(λ (selection #:editor ed)
(when (empty? (get-starts+ends ed))
(rectangle-selection selection #:editor ed))
(rectangle-copy selection #:editor ed #:deselect? #f)
(rectangle-delete selection #:editor ed)
#f))
(define-script rectangle-copy
#:label "Rectangle &Copy"
#:menu-path ("Sele&ction" "&Rectangle")
(λ (selection #:editor ed #:deselect? [deselect? #t])
(when (empty? (get-starts+ends ed))
(rectangle-selection selection #:editor ed))
(send the-clipboard set-clipboard-string
(string-join
(for/list ([rg (in-list (get-starts+ends ed))])
(send ed get-text (first rg) (second rg)))
"\n")
0)
(when deselect? (rectangle-deselect #f #:editor ed))
#f))
;; TODO
;; Paste as a rectangle, enables moving columns in a table (like Geany)
;; Split the clipboard selection on "\n" and insert on consecutive lines.
;; Possibly add lines if none left.
#;
(define-script rectangle-paste
#:label "Rectangle &Paste"
#:menu-path ("Sele&ction" "&Rectangle")
(λ (selection #:editor ed)
#f))
(module url2script-info racket/base
(provide filename url)
(define filename "rectangle-selection.rkt")
(define url "https://gist.github.com/Metaxal/5860b23973b99aac247ed998136a0fd4"))
@Metaxal
Copy link
Author

Metaxal commented Oct 8, 2022

Initial text:
Screenshot from 2022-10-09 13-30-28
Select with normal selection tool, but consider only the smaller rectangle formed of the top-left corner and bottom-right corner of the selection:
Screenshot from 2022-10-09 13-30-40
Click on Rectangle-cut, then use normal paste after the table:
Screenshot from 2022-10-09 13-31-07

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment