├── .Rbuildignore ├── .gitignore ├── .travis.yml ├── AlignAssign.Rproj ├── DESCRIPTION ├── NAMESPACE ├── R ├── .gitignore └── align_assign.R ├── README.Rmd ├── README.md ├── inst ├── media │ ├── Thumbs.db │ ├── demo.gif │ ├── demo2.gif │ └── demo_cursors.gif └── rstudio │ └── addins.dcf └── man ├── alignAssign.Rd ├── alignAssignArrow.Rd └── alignAssignEqual.Rd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^.travis.yml$ 4 | ^R/findAndReplaceAddin\.R$ 5 | ^R/ui\.R$ 6 | ^R/tst\.R$ 7 | ^R/utils\.R$ 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | cache: packages 3 | -------------------------------------------------------------------------------- /AlignAssign.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: AlignAssign 2 | Type: Package 3 | Title: Align the assignment operators within a highlighted area 4 | Version: 0.5.0 5 | Authors@R: c( 6 | person("Luke", "Smith", , "lukedansmi@sbcglobal.net", role = c("aut", "cre")), 7 | person("Garrick", "Aden-Buie", , role = "aut") 8 | ) 9 | URL: https://github.com/seasmith/AlignAssign 10 | BugReports: https://github.com/seasmith/AlignAssign/issues 11 | Description: A very simple aligner for a highlighted region's '<-' or '=' 12 | assignment operators. It does not "reflow" your code if the alignment 13 | breaks the page width. This addin also does not treat commented lines 14 | differently than uncommented lines. 15 | Imports: 16 | rstudioapi 17 | License: GPL-2 18 | Encoding: UTF-8 19 | LazyData: true 20 | RoxygenNote: 6.0.1 21 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(alignAssign) 4 | export(alignAssignArrow) 5 | export(alignAssignEqual) 6 | -------------------------------------------------------------------------------- /R/.gitignore: -------------------------------------------------------------------------------- 1 | findAndReplaceAddin.R 2 | tst.r 3 | ui.R 4 | utils.R 5 | -------------------------------------------------------------------------------- /R/align_assign.R: -------------------------------------------------------------------------------- 1 | capture <- function() { 2 | # Get context 3 | rstudioapi::getActiveDocumentContext() 4 | } 5 | 6 | captureArea <- function(capture) { 7 | # Find range 8 | range_start <- capture$selection[[1L]]$range$start[[1L]] 9 | range_end <- capture$selection[[1L]]$range$end[[1L]] 10 | 11 | # Dump contents and use highlighted lines as names. 12 | contents <- capture$contents[range_start:range_end] 13 | names(contents) <- range_start:range_end 14 | return(contents) 15 | } 16 | 17 | findRegEx <- function(find, where) { 18 | 19 | # Find matches, extract positions, find furthest <-, get rows/cols to align. 20 | matched.rows <- grep(find, where) 21 | positions <- regexec(find, where) 22 | positions <- positions[matched.rows] 23 | 24 | lines.highlighted <- as.integer(names(where)) 25 | matched.cols <- sapply(positions, `[[`, 1L) 26 | which.max.col <- which.max(matched.cols) 27 | 28 | furthest_row <- lines.highlighted[matched.rows[which.max.col]] 29 | furthest_column <- max(matched.cols) 30 | 31 | return(list(matched.rows = matched.rows, 32 | matched.cols = matched.cols, 33 | lines.highlighted = lines.highlighted, 34 | which.max.col = which.max.col, 35 | furthest_column = furthest_column)) 36 | } 37 | 38 | assembleInsert <-function(info) { 39 | # Unload variables 40 | matched.rows <- info$matched.rows 41 | matched.cols <- info$matched.cols 42 | lines.highlighted <- info$lines.highlighted 43 | which.max.col <- info$which.max.col 44 | furthest_column <- info$furthest_column 45 | 46 | # Find the rows to align and the current column position of each regEx match. 47 | rows_to_align <- lines.highlighted[matched.rows[-which.max.col]] 48 | columns_to_align <- matched.cols[-which.max.col] 49 | 50 | # Set location for spaces to be inserted. 51 | location <- Map(c, rows_to_align, columns_to_align) 52 | 53 | # Find and set the number of spaces to insert on each line. 54 | text_num <- furthest_column - columns_to_align 55 | text <- vapply(text_num, 56 | function(x) paste0(rep(" ", x), collapse = ""), 57 | character(1)) 58 | 59 | return(list(location = location, text = text)) 60 | } 61 | 62 | insertr <- function(list) { 63 | rstudioapi::insertText(list[["location"]], list[["text"]]) 64 | } 65 | 66 | #' Align a highlighted region's assignment operators. 67 | #' 68 | #' @param rgx_op Regex for assignment operator 69 | #' @return 70 | #' Aligns the given or guessed operator within a highlighted region. 71 | #' @export 72 | alignAssign <- function(rgx_op = NULL) { 73 | capture <- capture() 74 | area <- captureArea(capture) 75 | if (is.null(rgx_op)) rgx_op <- guess_operator(area) 76 | loc <- findRegEx(rgx_op, area) 77 | insertList <- assembleInsert(loc) 78 | insertr(insertList) 79 | } 80 | 81 | #' Align a highlighted region's assignment operators. 82 | #' 83 | #' @return Aligns the equal sign assignment operators (\code{=}) within a 84 | #' highlighted region. 85 | #' @export 86 | alignAssignEqual <- function() { 87 | alignAssign("=") 88 | } 89 | 90 | #' Align a highlighted region's assignment operators. 91 | #' 92 | #' @return Aligns the single caret operators (\code{<-}) within a 93 | #' highlighted region. 94 | #' @export 95 | alignAssignArrow <- function() { 96 | alignAssign("<-") 97 | } 98 | 99 | guess_operator <- function(area = captureArea(capture())) { 100 | area <- strsplit(area, "\n") 101 | counts <- list( 102 | "=" = vapply(gregexpr("=", area, fixed = TRUE), function(x) length(x[x > 0]), integer(1)), 103 | "<-" = vapply(gregexpr("<-", area, fixed = TRUE), function(x) length(x[x > 0]), integer(1)) 104 | ) 105 | # Does one appear in all? (keep) 106 | all_ones <- vapply(lapply(counts, function(x) x == 1), all, logical(1)) 107 | if (sum(all_ones) == 1) return(names(all_ones)[all_ones]) 108 | 109 | # Does only one appear at all? (keep) 110 | nones <- vapply(lapply(counts, function(x) x == 0), all, logical(1)) 111 | if (sum(nones) == 1) { 112 | return(names(counts)[!nones]) 113 | } else if (sum(nones) == 2) { 114 | stop("Neither `=` or `<-` are used in the selected lines") 115 | } 116 | 117 | # if not in all or none then are either duplicated on a line? (discard) 118 | mult_in_lines <- vapply(lapply(counts, function(x) x > 1), sum, integer(1)) 119 | if (sum(mult_in_lines) == 1) return(names(counts)[!mult_in_lines]) 120 | 121 | # fall back to max count 122 | some_ones <- vapply(lapply(counts, function(x) x == 1), sum, integer(1)) 123 | all_same <- length(unique(counts)) == 1 124 | if (!all_same) { 125 | return(names(which.max(some_ones))) 126 | } else { 127 | warning("Couldn't guess the operator for alignment, trying ` <- `") 128 | return("<-") 129 | } 130 | } 131 | 132 | alignCursor <- function() { 133 | context <- rstudioapi::getActiveDocumentContext() 134 | 135 | cursors <- lapply(context$selection, function(x) { 136 | rbind(x$range$start, x$range$end) 137 | }) 138 | 139 | if (length(cursors) < 2) { 140 | message("Nothing to align, did you place multiple cursors in the document?") 141 | return() 142 | } 143 | 144 | x <- as.data.frame(do.call("rbind", cursors)) 145 | x <- unique(x) 146 | x <- x[order(x$row), ] 147 | 148 | # used to keep track of added space if multiple cursors per line 149 | added_spaces <- data.frame(row = unique(x$row), nt = 0L) 150 | 151 | x$group <- sequence(rle(x$row)$lengths) 152 | x <- split(x, x$group) 153 | for (xg in x) { 154 | xg <- merge(xg, added_spaces, by = "row") 155 | xg$column <- xg$column + xg$nt 156 | xg$n <- max(xg$column) - xg$column 157 | added_spaces <- update_spaces(added_spaces, xg) 158 | spaces_to_add <- make_space(xg$n) 159 | locs <- Map(c, xg$row, xg$column) 160 | rstudioapi::insertText(locs, spaces_to_add, id = context$id) 161 | } 162 | } 163 | 164 | make_space <- function(n) { 165 | vapply(n, function(nn) strrep(' ', nn), " ") 166 | } 167 | 168 | update_spaces <- function(a, x) { 169 | a <- merge(a, x[, c("row", "n")], by = "row") 170 | a$nt <- a$nt + x$n 171 | a[, c("row", "nt")] 172 | } 173 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | # AlignAssign 6 | 7 | [![Build Status](https://travis-ci.org/seasmith/AlignAssign.svg?branch=master)](https://travis-ci.org/seasmith/AlignAssign) 8 | 9 | Align the assignment operators (either `<-` or `=`) within a highlighted area. 10 | 11 | Before: 12 | ```{r, eval=FALSE} 13 | a <- 1:5 14 | bbb <- 6:10 15 | c <- letters 16 | ``` 17 | 18 | After: 19 | ```{r, eval=FALSE} 20 | a <- 1:5 21 | bbb <- 6:10 22 | c <- letters 23 | ``` 24 | 25 | ![](inst/media/demo2.gif) 26 | 27 | #### Align Cursors 28 | 29 | For alignment of any text, you can align multiple cursors with the **Align Cursors** addin. 30 | Add multiple cursors to your document by holding Ctrl/Cmd + Alt and clicking in the RStudio editor, or by holding Alt and clicking and dragging the mouse. 31 | 32 | ![](inst/media/demo_cursors.gif) 33 | 34 | ### What 35 | AlignAssign contains three addins. Two addins align all of either the `<-` (`Align Assign`) or `=` (`Align Assign 2`) assignment operators within a highlighted region and the third aligns multiple cursors across lines to the same column. 36 | 37 | None of the addins "reflow" your code if the alignment breaks the page width. They also does not treat commented lines differently to uncommented lines. __If there is either one of the assignment operators within a highlighted comment line, then it will either align that operator or align other operators to it.__ 38 | 39 | ## Install 40 | ```{r install, eval=FALSE} 41 | devtools::install_github("seasmith/AlignAssign")` 42 | ``` 43 | 44 | You can assign each alignment addin action to a specific keyboard shortcut in RStudio in the *Modify keyboard shortcuts...* menu option under the *Tools* menu. 45 | 46 | ## Examples 47 | 48 | #### Align `<-`'s with Align Assign 49 | When you highlight the following chunk of code (region) - whether you highlight the entirity or just a portion of the first and last lines - and then run the `Align Assign` addin... 50 | ```{r, eval=FALSE} 51 | # This is a commented line 52 | # So is this 53 | a <- 1:5 54 | b <- 6:10 55 | copy_a <- a 56 | # More comments 57 | ``` 58 | 59 | ...the result will look like this. 60 | ```{r, eval=FALSE} 61 | # This is a commented line 62 | # So is this 63 | a <- 1:5 64 | b <- 6:10 65 | copy_a <- a 66 | # More comments 67 | ``` 68 | 69 | #### Align `=`'s with Align Assign 2 70 | The above example also works for the `=` operator when using the other addin, `Align Assign 2`. Before... 71 | ```{r, eval=FALSE} 72 | # Perosnal information 73 | list(surname = "Crichton", 74 | firstName = "John", 75 | address = NA, 76 | occupation = "fugitive") 77 | ``` 78 | 79 | ...after. 80 | ```{r, eval=FALSE} 81 | # Perosnal information 82 | list(surname = "Crichton", 83 | firstName = "John", 84 | address = NA, 85 | occupation = "fugitive") 86 | ``` 87 | 88 | #### Behavior of commented-out assignment operators 89 | Be mindful that highling a chunk of code which has assignment operators within commented lines, like the following, and running the `Align Assign 2` addin... 90 | ```{r, eval=FALSE} 91 | # This is a commented line with an assignment operator <- 92 | a <- 1:5 93 | b <- 6:10 94 | c <- 11:15 95 | # There is an assignment operator <- here, too 96 | ``` 97 | 98 | ...will result in something like this. 99 | ```{r, eval=FALSE} 100 | # This is a commented line with an assignment operator <- 101 | a <- 1:5 102 | b <- 6:10 103 | c <- 11:15 104 | # There is an assignment operator <- here, too 105 | ``` 106 | 107 | #### Not so smart aligner 108 | There is also no special handling of assignment operators within a function. So, if you highlighted the entire chunk below and then ran the `Align Assign` addin... 109 | ```{r, eval=FALSE} 110 | var1 <- letters 111 | var2 <- as.list(sample(1:26, 26)) 112 | names(var2) <- var1[unlist(var2)] 113 | list.pos <- function(name, lst){ 114 | matches <- sapply(name, function(x){ 115 | matched <- which(names(lst) %in% x) 116 | 117 | if(length(matched) == 0) matched <- NA 118 | matched 119 | }) 120 | return(matches) 121 | } 122 | positions <- list.pos(c("a", "bbb", "c"), var2) 123 | ``` 124 | 125 | ...the result will look like this. 126 | ```{r, eval=FALSE} 127 | var1 <- letters 128 | var2 <- as.list(sample(1:26, 26)) 129 | names(var2) <- var1[unlist(var2)] 130 | list.pos <- function(name, lst){ 131 | matches <- sapply(name, function(x){ 132 | matched <- which(names(lst) %in% x) 133 | 134 | if(length(matched) == 0) matched <- NA 135 | matched 136 | }) 137 | return(matches) 138 | } 139 | positions <- list.pos(c("a", "bbb", "c"), var2) 140 | ``` 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # AlignAssign 3 | 4 | [![Build 5 | Status](https://travis-ci.org/seasmith/AlignAssign.svg?branch=master)](https://travis-ci.org/seasmith/AlignAssign) 6 | 7 | Align the assignment operators (either `<-` or `=`) within a highlighted 8 | area. 9 | 10 | Before: 11 | 12 | ``` r 13 | a <- 1:5 14 | bbb <- 6:10 15 | c <- letters 16 | ``` 17 | 18 | After: 19 | 20 | ``` r 21 | a <- 1:5 22 | bbb <- 6:10 23 | c <- letters 24 | ``` 25 | 26 | ![](inst/media/demo2.gif) 27 | 28 | #### Align Cursors 29 | 30 | For alignment of any text, you can align multiple cursors with the 31 | **Align Cursors** addin. Add multiple cursors to your document by 32 | holding Ctrl/Cmd + Alt and clicking in 33 | the RStudio editor, or by holding Alt and clicking and 34 | dragging the mouse. 35 | 36 | ![](inst/media/demo_cursors.gif) 37 | 38 | ### What 39 | 40 | AlignAssign contains three addins. Two addins align all of either the 41 | `<-` (`Align Assign`) or `=` (`Align Assign 2`) assignment operators 42 | within a highlighted region and the third aligns multiple cursors across 43 | lines to the same column. 44 | 45 | None of the addins “reflow” your code if the alignment breaks the page 46 | width. They also does not treat commented lines differently to 47 | uncommented lines. **If there is either one of the assignment operators 48 | within a highlighted comment line, then it will either align that 49 | operator or align other operators to it.** 50 | 51 | ## Install 52 | 53 | ``` r 54 | devtools::install_github("seasmith/AlignAssign")` 55 | ``` 56 | 57 | You can assign each alignment addin action to a specific keyboard 58 | shortcut in RStudio in the *Modify keyboard shortcuts…* menu option 59 | under the *Tools* menu. 60 | 61 | ## Examples 62 | 63 | #### Align `<-`’s with Align Assign 64 | 65 | When you highlight the following chunk of code (region) - whether you 66 | highlight the entirity or just a portion of the first and last lines - 67 | and then run the `Align Assign` addin… 68 | 69 | ``` r 70 | # This is a commented line 71 | # So is this 72 | a <- 1:5 73 | b <- 6:10 74 | copy_a <- a 75 | # More comments 76 | ``` 77 | 78 | …the result will look like this. 79 | 80 | ``` r 81 | # This is a commented line 82 | # So is this 83 | a <- 1:5 84 | b <- 6:10 85 | copy_a <- a 86 | # More comments 87 | ``` 88 | 89 | #### Align `=`’s with Align Assign 2 90 | 91 | The above example also works for the `=` operator when using the other 92 | addin, `Align Assign 2`. Before… 93 | 94 | ``` r 95 | # Perosnal information 96 | list(surname = "Crichton", 97 | firstName = "John", 98 | address = NA, 99 | occupation = "fugitive") 100 | ``` 101 | 102 | …after. 103 | 104 | ``` r 105 | # Perosnal information 106 | list(surname = "Crichton", 107 | firstName = "John", 108 | address = NA, 109 | occupation = "fugitive") 110 | ``` 111 | 112 | #### Behavior of commented-out assignment operators 113 | 114 | Be mindful that highling a chunk of code which has assignment operators 115 | within commented lines, like the following, and running the `Align 116 | Assign 2` addin… 117 | 118 | ``` r 119 | # This is a commented line with an assignment operator <- 120 | a <- 1:5 121 | b <- 6:10 122 | c <- 11:15 123 | # There is an assignment operator <- here, too 124 | ``` 125 | 126 | …will result in something like this. 127 | 128 | ``` r 129 | # This is a commented line with an assignment operator <- 130 | a <- 1:5 131 | b <- 6:10 132 | c <- 11:15 133 | # There is an assignment operator <- here, too 134 | ``` 135 | 136 | #### Not so smart aligner 137 | 138 | There is also no special handling of assignment operators within a 139 | function. So, if you highlighted the entire chunk below and then ran the 140 | `Align Assign` addin… 141 | 142 | ``` r 143 | var1 <- letters 144 | var2 <- as.list(sample(1:26, 26)) 145 | names(var2) <- var1[unlist(var2)] 146 | list.pos <- function(name, lst){ 147 | matches <- sapply(name, function(x){ 148 | matched <- which(names(lst) %in% x) 149 | 150 | if(length(matched) == 0) matched <- NA 151 | matched 152 | }) 153 | return(matches) 154 | } 155 | positions <- list.pos(c("a", "bbb", "c"), var2) 156 | ``` 157 | 158 | …the result will look like this. 159 | 160 | ``` r 161 | var1 <- letters 162 | var2 <- as.list(sample(1:26, 26)) 163 | names(var2) <- var1[unlist(var2)] 164 | list.pos <- function(name, lst){ 165 | matches <- sapply(name, function(x){ 166 | matched <- which(names(lst) %in% x) 167 | 168 | if(length(matched) == 0) matched <- NA 169 | matched 170 | }) 171 | return(matches) 172 | } 173 | positions <- list.pos(c("a", "bbb", "c"), var2) 174 | ``` 175 | -------------------------------------------------------------------------------- /inst/media/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seasmith/AlignAssign/1811a06847825868bc655a1c64781c94e797a3da/inst/media/Thumbs.db -------------------------------------------------------------------------------- /inst/media/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seasmith/AlignAssign/1811a06847825868bc655a1c64781c94e797a3da/inst/media/demo.gif -------------------------------------------------------------------------------- /inst/media/demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seasmith/AlignAssign/1811a06847825868bc655a1c64781c94e797a3da/inst/media/demo2.gif -------------------------------------------------------------------------------- /inst/media/demo_cursors.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seasmith/AlignAssign/1811a06847825868bc655a1c64781c94e797a3da/inst/media/demo_cursors.gif -------------------------------------------------------------------------------- /inst/rstudio/addins.dcf: -------------------------------------------------------------------------------- 1 | Name: Align Assign 2 | Description: Aligns '<-' or '=' assignment operators within a highlighted area. 3 | Binding: alignAssign 4 | Interactive: false 5 | 6 | Name: Align Cursors 7 | Description: Aligns all cursors. Use Ctrl/Cmd + Alt + click to insert multiple cursors. 8 | Binding: alignCursor 9 | Interactive: false 10 | 11 | Name: Align Assign Arrow 12 | Description: Aligns '<-' assignment operators within a highlighted area. 13 | Binding: alignAssignArrow 14 | Interactive: false 15 | 16 | Name: Align Assign Equal 17 | Description: Aligns assignment '=' operators within a highlighted area. 18 | Binding: alignAssignEqual 19 | Interactive: false 20 | -------------------------------------------------------------------------------- /man/alignAssign.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/align_assign.R 3 | \name{alignAssign} 4 | \alias{alignAssign} 5 | \title{Align a highlighted region's assignment operators.} 6 | \usage{ 7 | alignAssign(rgx_op = NULL) 8 | } 9 | \arguments{ 10 | \item{rgx_op}{Regex for assignment operator} 11 | } 12 | \value{ 13 | Aligns the given or guessed operator within a highlighted region. 14 | } 15 | \description{ 16 | Align a highlighted region's assignment operators. 17 | } 18 | -------------------------------------------------------------------------------- /man/alignAssignArrow.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/align_assign.R 3 | \name{alignAssignArrow} 4 | \alias{alignAssignArrow} 5 | \title{Align a highlighted region's assignment operators.} 6 | \usage{ 7 | alignAssignArrow() 8 | } 9 | \value{ 10 | Aligns the single caret operators (\code{<-}) within a 11 | highlighted region. 12 | } 13 | \description{ 14 | Align a highlighted region's assignment operators. 15 | } 16 | -------------------------------------------------------------------------------- /man/alignAssignEqual.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/align_assign.R 3 | \name{alignAssignEqual} 4 | \alias{alignAssignEqual} 5 | \title{Align a highlighted region's assignment operators.} 6 | \usage{ 7 | alignAssignEqual() 8 | } 9 | \value{ 10 | Aligns the equal sign assignment operators (\code{=}) within a 11 | highlighted region. 12 | } 13 | \description{ 14 | Align a highlighted region's assignment operators. 15 | } 16 | --------------------------------------------------------------------------------