├── ALE.dws ├── .gitattributes ├── LICENSE.txt └── README.md /ALE.dws: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arcfide/ALE/HEAD/ALE.dws -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Aaron W. Hsu 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NAME 2 | 3 | **ALE** – arcfide's line editor 4 | 5 | # DESCRIPTION 6 | 7 | **ALE** is a line editor that I have created because I can. One of my all time 8 | favorite editors to use in the classic Unix *ed(1)* editor. Line editors are, 9 | in my opinion, grossly under-appreciated for their workflow and power as 10 | interactive editors. With that said (or should I say, sed?), the pre-eminent 11 | example of this in the modern computing world, *ed(1)*, does have a few issues 12 | that I felt I could improve upon. 13 | 14 | Furthermore, I was tired of the infrastructure setup that is required in order 15 | to use a line editor inside of modern operating systems. They aren't convenient 16 | to use by default in GUI environments, and there isn't a good, clean, fast way 17 | to "just get" *ed(1)* on Windows, because so much of *ed(1)*'s design relies on 18 | having the wider UNIX ecosystem available to it. This means, really, that if I 19 | wanted to use *ed(1)* on Windows, I was more or less resigned to also installing 20 | a rather heavyweight UNIX environment as well. This greatly interferes with the 21 | simplicity of the editor itself, as well as integration. 22 | 23 | Since almost all of my work is done with APL, I realized the potential value of 24 | having an easy to use line editor that was written in a deeply integrated way 25 | with the APL language. APL is itself a pretty good textual editing language, and 26 | by simply creating a "dictionary" of editing commands, I could leverage the 27 | power of the existing Dyalog APL session to do almost all the heavy lifting for 28 | me. 29 | 30 | The result is an editor that retains the benefits of the *ed(1)* editor with 31 | some noticeable improvements in flexibility, while simultaneously making the 32 | editor itself simpler, easier to maintain, and amenable to customized 33 | extensions, macros, and other composability and programmability benefits 34 | usually only seen in more complex editors, all with basically zero real 35 | additional complexity. 36 | 37 | In short, **ALE** is a programmable, composable, but simpler and more 38 | streamlined, syntactically consistent replacement for the venerable 39 | *ed(1)*. 40 | 41 | # LINE ADDRESSING 42 | 43 | Lines are origin-1 based references to lines in a file, with the 0th 44 | line referencing the line before the first line in a file. Address 45 | ranges are a pair of lines where the first line must be less than or equal 46 | to the second line. They specify a contiguous region of lines in a file. 47 | 48 | Traditional ed(1) had a number of different addressing shortcuts. 49 | We do not have any of that in ALE at the moment, and instead prefer to 50 | rely on the fact that we have full arithmetic expressive power through 51 | APL. This means that the number of built-in addressing short cuts is 52 | relatively limited. Instead, you should simply write an APL expression 53 | to give you the appropriate range if you should want it. 54 | 55 |
56 |
_
57 |
The address of the current line.
58 |
_a
59 |
Equivalent to `1,≢∆`.
60 |
_s regex
61 |
Gives the next line matching regex with wraparound.
62 |
_r regex
63 |
Gives the previous line matching regex with wraparound.
64 |
65 | 66 | # COMMMANDS 67 | 68 |
69 |
a line
70 |
Appends text to the buffer after the addressed line. Input mode is used 71 | to enter text. The current address is set to the last line entered.
72 | 73 |
c range
74 |
Change the addressed range in the buffer. Replacement text is entered 75 | in input mode. The current address is set to the last line entered.
76 | 77 |
d range
78 |
Deletes the addressed lines and sets the current address to the line 79 | after the deleted line if there is one, or to the line before the deleted 80 | range if there is not.
81 | 82 |
e file
83 |
Edits file and sets the default filename. Clears and replaces 84 | the existing edit buffer. The current address is set to the last line read. 85 |
86 | 87 |
efn expr
88 |
Edits the vector of strings returned by expr. The current 89 | address is set to the last line read. This function replaces the use of 90 | the shell commands with APL expressions.
91 | 92 |
E file
93 |
Unconditionally edits file. This is like e but 94 | it will erase a dirty buffer without warning.
95 | 96 |
(1,≢∆){...}g 're'
97 |
Applies the function `{...}` to the addressed lines matching 98 | regular expression `'re'`. For each match, the function is applied once 99 | after setting the current line to the matching line. The right argument 100 | to the function is the line number of the match. The line number is set 101 | to the last line modified by one of the commands issued inside of the 102 | function.
103 | 104 |
(1,≢∆)G 're'
105 |
Works much like the `g` command except that it prompts 106 | interactively for the commands to run. It does this for each matching 107 | line by first printing the line that matches, and then prompting with 108 | the evaluator to ask for the next commands to run. The format of the 109 | commands must be suitable for execution by the `eval` function.
110 | 111 |
i line
112 |
Insert text before the addressed line. Text is entered in input mode. 113 | The current address is set to the last line entered.
114 | 115 |
j range
116 |
Joins the addressed range. The range is replaced with a single line 117 | containing the joined lines. The current address is set to the joined 118 | line.
119 | 120 |
line k string
121 |
Marks the addressed line with the given string. Deleting or 122 | modifying the line will result in that mark being deleted. Commands that 123 | support it may use string in place of a line number.
124 | 125 |
l range
126 |
Print the addressed lines unambiguously. Otherwise works like the 127 | `p` command.
128 | 129 |
range m line
130 |
Moves the addressed range to right after the addressed line. The current 131 | address is set to the last line moved.
132 | 133 |
n range
134 |
Prints the range with line numbers. The current address is set to the 135 | last line printed.
136 | 137 |
p range
138 |
Prints the range. The current address is set to the last line printed.
139 | 140 |
q
141 |
Quits **ALE**.
142 | 143 |
Q
144 |
Quites **ALE** unconditionally.
145 | 146 |
line r file
147 |
Read file into the buffer after line. Sets the default filename to file 148 | if there was no default filename already set. Sets the current address to the 149 | last line read.
150 | 151 |
line r expr
152 |
Works the same way as the r command with a filename, but 153 | if the right argument to r is a vector of strings, then this 154 | will be used as the contents to read into the buffer. It does not set the 155 | filename, and the current address is set to the last line read.
156 | 157 |
(_,_)s pat rep pos
158 |
Performs regex substitution. The pat value is a string 159 | containing the regex to match against. The rep string is a 160 | regex replacement pattern. The optional pos argument indicates 161 | how many replacements to do, the default being 0, meaning all possible 162 | replacements. If a non-zero, positive value n is given, then 163 | only the first n values will be replaced per line. If a negative 164 | value -n is given, then only the specific nth value will 165 | be replaced. The current line is set to the last line that was modified.
166 | 167 |
(1,≢∆)t line
168 |
Transfers (copies) the addressed range to right after the addressed 169 | lines. Current address is set to the last line transferred.
170 | 171 |
u
172 |
Undoes the effect of the last command that modified anything in the buffer 173 | and restores the current address to what it was before the command. The 174 | global commands 'g', 'G', 'v', and 'V' are treated as a single command by 175 | undo. 'u' is its own inverse; it can undo only the last command.
176 | 177 |
(1,≢∆){...}v 're'
178 |
Does the same thing as the `g` command but applies the 179 | function to lines that do *not* match 're' instead of those 180 | that do.
181 | 182 |
(1,≢∆)V 're'
183 |
Behaves like `G`, but like the `v` command, it operates on the 184 | not matching lines, rather than the matching lines.
185 | 186 |
(1,≢∆)w file
187 |
Writes the addressed lines to file. Previous contents of 188 | file are clobbered without warning. If there is no default filename, 189 | then the default filename will be set to file, otherwise it is not 190 | changed.
191 | 192 |
(1,≢∆)wq file
193 |
Writes buffer to file as with w and then executes 194 | the q command.
195 | 196 |
(1,≢∆)W file
197 |
Works like the `w` command but appends to the end of the file 198 | instead of overwriting the file.
199 | 200 |
(_+1)z n
201 |
Scrolls n lines at a time starting at the addressed line. 202 | The current address is set to the last line printed.
203 |
204 | 205 | **NOTE**: For any of the regex-consuming commands above—namely *_r*, *_s*, 206 | *s*, *g*, and *v*—supplying the null regex `''` is equivalent to supplying the 207 | previously-used regex. This "regex history" is shared among all the commands 208 | and is initialized to the null regex. 209 | 210 | 211 | # COMPARING ED(1) TO ALE 212 | 213 | | ed(1) | ALE | Comments 214 | | ----------------------- | ------------- | -------- 215 | | . | _ | Current line 216 | | (.)a | a line | 217 | | (.,.)c | c range | 218 | | (.,.)d | d range | 219 | | e file | e 'file' | 220 | | e !command | efn expr | Use APL, not *sh(1)*, expr must return vector of strings 221 | | E file | E 'file' | 222 | | f | ⍙ | Prints default filename 223 | | f file | ⍙←'file' | Sets default filename 224 | | (1,$)g/re/command-list | (1,≢∆){...}g're' | ⍵ is _ for each match 225 | | (1,$)G/re/ | (1,≢∆)G're' | 226 | | H | N/A | **ALE** errors are more verbose 227 | | h | N/A | See H 228 | | (.)i | i line | 229 | | (.,.+1)j | j range | 230 | | (.)k lc | line k string | 231 | | (.,.)l | l range | 232 | | (.,.)m(.) | range m line | 233 | | (.,.)n | n range | 234 | | (.,.)p | p range | 235 | | P | N/A | APL session prompt is used 236 | | q | q | 237 | | Q | )off | 238 | | ($)r file | line r file | 239 | | ($)r !command | line r expr | `expr` must return vector of strings 240 | | (.,.)s/re/replacement/ | (_,_)s 're' 'replacement' 1 | 241 | | (.,.)s/re/replacement/g | (_,_)s 're' 'replacement' | 242 | | (.,.)s/re/replacement/n | (_,_)s 're' 'replacement' ¯n | 243 | | (.,.)s | N/A | Use the history mechanism 244 | | (.,.)t(.) | range t line | 245 | | u | | Not yet implemented 246 | | (1,$)v/re/command-list | range {...}v 're' | 247 | | (1,$)V/re/ | range V 're' | 248 | | (1,$)w file | (1,≢∆)w file | Use ⍙ for default file 249 | | (1,$)wq file | (1,≢∆)wq file | Use ⍙ for default file 250 | | (1,$)w !command | fn ∆ | Apply APL function to buffer 251 | | (1,$)W file | (1,≢∆)W file | Use ⍙ for default file 252 | | (.+1)z n | (_+1)z n | 253 | | ($)= | N/A | Ranges print themselves 254 | | (.+1)newline | N/A | Use z instead 255 | | !command | ⎕SH 'command' | See Dyalog Documentation on ⎕SH 256 | 257 | # TIPS AND TRICKS 258 | 259 | Because **ALE** is basically just a slightly optimized APL session, you have 260 | at your disposal the full power of APL. This leads to some interesting features 261 | that **ALE** can "have" if you want it to. 262 | 263 | # FILES 264 | 265 | **ALE** does not currently use a file buffer. This means that files are loaded 266 | into memory and stored there. This limits the size of files that can be loaded 267 | to the size of your main memory and the swap space that you have available on 268 | your machine. 269 | 270 | # SEE ALSO 271 | 272 | [The OpenBSD ed(1) man page](https://man.openbsd.org/ed) 273 | 274 | # DIAGNOSTICS 275 | 276 | All commands return an error code as the shy result of their execution. A 0 277 | value indicates success, and a non-zero value indicates the type of error that 278 | occured. 279 | 280 | # CAVEATS 281 | 282 | **ALE** is very new software right now, and it may not be fully stable. Beware 283 | of this before using it in a mission critical environment. 284 | --------------------------------------------------------------------------------