Last active
October 9, 2022 13:38
-
-
Save Metaxal/5860b23973b99aac247ed998136a0fd4 to your computer and use it in GitHub Desktop.
A quickscript to select a rectangle of text, like Geany
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Initial text:
Select with normal selection tool, but consider only the smaller rectangle formed of the top-left corner and bottom-right corner of the selection:
Click on Rectangle-cut, then use normal paste after the table: