Skip to content

Instantly share code, notes, and snippets.

@lewdev
Last active June 26, 2026 18:57
Show Gist options
  • Select an option

  • Save lewdev/c56049adb2516f877d624c7b16a242ed to your computer and use it in GitHub Desktop.

Select an option

Save lewdev/c56049adb2516f877d624c7b16a242ed to your computer and use it in GitHub Desktop.
πŸ’» Address Bar Code Editor Bookmarklet Part 5 - Goto Line and Tab In/Out

πŸ’» Address Bar Code Editor Bookmarklet Part 5 (2089b) - Goto Line and Tab In/Out

New Features:

  • Press Ctrl + L to get prompted to enter a line number which it will go to. It will highlight the entire line for visibility.
    • I didn't use Ctrl + G because it interfered with the browser's search again key shortcut.
  • Tab or Shift + Tab to indent or remove indents with two spaces. Works with highlighting multiple lines.
  • Ctrl + D duplicate line(s). Works with highlighting multiple lines.
  • Ctrl + Up/Down scroll
  • Ctrl + S save to file
  • Enclose highlighted text with:
    ()[]{}""''``
    
  • Implement custom Undo Ctrl + Z and Redo Ctrl + Y functionality because tabbing and duplicating lines is not a text operation where the browser's undo/redo can do it properly.

Sorry that I am "team spaces" over "tabs" but this bugged me. I figured I could copy-paste my code into another editor to apply the tabs, but that took too much time and saw that it wasn't too much effort to implement it as a feature.

This update increased the code by over 1kb! It's a lot, but the features will improve the quality of life so much.

Terse the code!

The code got fairly complicated such that I couldn't write it in this minified format. So I wrote it up and then used Terser Online by xem to get it minified. It's a simple Terser so it won't mess with my code except to apply some code minifying techniques. It's awesome and I almost forgot it existed. Such an awesome tool!

Adding new features

I also forgot that I started implementing the "Goto Line" feature in "Part 1" and got it working perfectly. It's kind of fun knowing how to take advantage of a textarea and give it features. I'm pretty sure I can add in autocomplete features too, I just can't think of any meaningful implementations at the moment.

I am hesitant to implement "Find & Replace" because there is already a text search feature on your browser which does text highlighting, but it will not do a replace. I haven't felt that it hinders the little coding projects I do on this tiny app.

There is a balance I want to achieve here. This tiny app is not meant to replace a coding app which will basically do everything else much better. It should allow me to create a tiny prototype of something and then move it on to a VSCode project when it gets sufficiently complex. I could add more features, but how badly do I need them before I can simply switch the editor to something else? Or should I implement features "because I can"?

Anyway, I hope others find this useful!

The Code

Copy-paste into your address bar or into a bookmark to save it for later use.

data:text/html,<title>code</title><link rel=icon href='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 99 99"><text y="85" font-size="72">&%23128187;</text></svg>'><body id=b><textarea id=d spellcheck=false style=resize:none;padding-bottom:2em><style>:root{color-scheme:dark}</style>%0d<p id=o></p>%0d<script>%0do.innerHTML = 0;%0d</script></textarea><iframe id=f></iframe><p id=p></p><script>h=Q=N=0,S="selection",O=(e,l=e)=>(A=d[S+"Start"]=e,B=d[S+"End"]=l),F=e=>e.split`\n`,J=e=>e.join`\n`,W=[d.value],M="()[]{}\"\"''``",X=e=>V=d.value=e,d.onkeydown=d.onclick=e=>(P=l=>e.preventDefault(),A=d[S+"Start"],B=d[S+"End"],V=d.value,H=e=>V.substr(0,e),K=e.key,C=e.keyCode,I=e.shiftKey,U=38==C,D=40==C,R=F(V),T=R.length,u=0,G=e=>F(V).reduce((l,t,n)=>(n<e?t.length+1:0)+l,0),Y=F(H(A)).length-2,E=F(H(B)),Z=E.length,10==C&&(f.srcdoc=V),e.ctrlKey&&(13==C&&(f.srcdoc=V),"l"==K&&(P(),L=parseInt(prompt("Goto line")),L=L<0?0:L<T?L:T,m=J(R.slice(0,L)).length,O(m-R[L-1].length,m),E=F(H(B))),"d"==K&&(P(),R.splice(Y+1,0,J(R.filter((e,l)=>l>Y&&l<Z))),X(J(R)),O(G(Y+1),G(Z)-1)),(U||D)&&P(d.scrollTop+=U?-15:D?15:0),"s"==K&&(P(a=document.createElement`a`),(m=prompt("Filename","f.html"))&&(a.href=URL.createObjectURL(new Blob([V],{type:"text/plain"})),a.click(a.download=m),URL.revokeObjectURL(a.href))),((k="z"==K)&&N>0||"y"==K&&N<W.length-1)&&(P(),v=W[k?--N:++N],g=V.length-v.length,V=d.value=v,O(A-g),u=1)),"Tab"==K&&(P(),X(J(R.map((e,l)=>l>Y&&l<Z?I?e.replace(/^ {1,2}/,""):"  "+e:e))),O(G(Y+1),G(Z)-1)),(k=M.indexOf(K))%2==0&&(P(),X(V.slice(0,A)+K+V.slice(A,B)+M[k+1]+V.slice(B)),O(A+1,B+1)),V!=W[N]&&!u&&(W=W.slice(0,N+1),W[++N]=V),p.innerHTML="<button onclick=d.click(b.className='mhqr'[++h%4])>H</button> "+(h%4==3?"":`Ln:${E.length}, Col:${E.pop().length+1}, ${A==B?"Pos:"+B:"Sel:"+(B-A)}, Len:${V.length}`))</script><style>*{color-scheme:dark;box-sizing:border-box;margin:0}%23d,%23f{width:50%;height:100%;vertical-align:top}.h>%23d,.h>%23f{width:100%;height:50%}%23p{position:fixed;bottom:1;background:%23333}.q>%23d,.r>%23f{width:100%}.r>%23d,.q>%23f{display:none}

Start your own Editor in 265b!

To apply the dark mode, you can add color-scheme:dark to the * styling. Otherwise, this is a bare-bones version of an HTML editor with the textarea and iframe sharing half the screen vertically. I could get this even smaller, but I think I've narrowed it down the fewest features without it being too difficult to read and understand the code.

data:text/html,<textarea id=d spellcheck=false onkeypress=e=event;k=e.keyCode;(e.ctrlKey&&k==13)||k==10?f.srcdoc=d.value:0 style=resize:none></textarea><iframe id=f></iframe><style>*{box-sizing:border-box;margin:0}%23d,%23f{width:50%;height:100%;vertical-align:top}

Note on Keyshortcuts

I'm not sure if I should override an existing browser keyshortcut or use one that is unused. Turns out there are only two Ctrl keyshortcuts that are unused and that is:

  • Ctrl + B
  • Ctrl + M

Previous Parts

<!DOCTYPE html>
<head>
<link rel=icon href='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 99 99"><text y="85" font-size="72">&#128187;</text></svg>'>
<title>code</title>
<style>
*{color-scheme:dark;box-sizing:border-box;margin:0}
#d,#f{width:calc(50% - 2px);height:calc(100vh - 2px);vertical-align:top}
.h>#d,.h>#f{width:100vh;height:50vh}
#p{position:fixed;bottom:1px;background:#333}
.q>#d,.r>#f{width:100%}
.r>#d,.q>#f{display:none}
</style>
</head>
<body id=b>
<textarea id=d spellcheck=false style=resize:none;padding-bottom:2em>
close
stop
focus
blur
open
alert
confirm
prompt
1 print
2 postMessage
3 captureEvents
4 releaseEvents
5 getSelection
6 getComputedStyle
7 matchMedia
8 moveTo
9 moveBy
0 resizeTo
resizeBy
scroll
scrollTo
scrollBy</textarea>
<iframe id=f></iframe>
<p id=p></p>
<script>
// h = changing layout
// Q = setTimeout
// N = edit history index
h = Q = N = 0,
// set cursor & highlighted positions
S = "selection",
O = (a, b = a) => (A = d[S + "Start"] = a, B = d[S + "End"] = b),
F = s => s.split`\n`,
J = a => a.join`\n`,
W = [d.value], // edit history
M = "()[]{}\"\"''``",
X = v => V = d.value = v,
d.onkeydown = d.onclick = e => (
P = _ => e.preventDefault(),
A = d[S + "Start"],
B = d[S + "End"],
V = d.value,
H = n => V.substr(0, n),
K = e.key,
C = e.keyCode,
I = e.shiftKey,
U = C == 38, // up key pressed
D = C == 40, // down key pressed
R = F(V),
T = R.length,
u = 0,
// get cursor position by line
G = L => F(V).reduce((t, l, i) => (i < L ? l.length + 1 : 0) + t, 0),
Y = F(H(A)).length - 2, //selected start line #
E = F(H(B)),
Z = E.length, // selected end line #
// Ctrl + Enter runs code
C == 10 && (f.srcdoc = V),
e.ctrlKey && (
// Ctrl + Enter runs code
C == 13 && (f.srcdoc = V),
// goto line by pressing Ctrl + L
"l" == K && (
P(),
L = parseInt(prompt("Goto line")),
L = L < 0 ? 0 : L < T ? L : T,
m = J(R.slice(0, L)).length,
O(m - R[L - 1].length, m),
E = F(H(B))
),
// duplicate line
"d" == K && (
P(),
R.splice(Y + 1, 0, J(R.filter((_, i) => i > Y && i < Z))),
X(J(R)),
O(G(Y + 1), G(Z) - 1)
),
// scroll up/down
(U || D) && P(d.scrollTop += U ? -15 : D ? 15 : 0),
// move line up/down (WIP)
/*
(I && (U || D)) && (
P(),
n = U ? -1 : 1,
(U && Y + 1 > 0 || D && Z < T) && (
V = d.value = (U ? R.reduce((t, l, i) => (
(i >= Y + 1 && i < Z) && (
W = t[i],
t[i] = t[i + n],
t[i + n] = W
),
t
), R) : R.map((l, k) => (
i = T - k,
(i >= Y + 1 && i < Z) && (
W = R[i + n],
R[i + n] = R[i],
R[i] = W
),
R[k]
)).join`\n`,
O(G(Y + 1 + n), G(Z + n) - 1),
E = F(V.substr(0, A))
))
),
*/
// save as a file
"s" == K && (
P(a = document.createElement`a`),
(m = prompt("Filename", "f.html")) && (
a.href = URL.createObjectURL(new Blob([V], { type: "text/plain" })),
a.click(a.download = m),
URL.revokeObjectURL(a.href)
)
),
// undo & redo
(((k = "z" == K) && N > 0) || ("y" == K && N < W.length - 1)) && (
P(),
v = W[k ? --N : ++N], // new value
f = V.length - v.length, // new and old value length diff
V = d.value = v,
O(A - f), // try to keep the cursor where it was
// TODO: I can improve by moving cursor to where the change happened
u = 1 // don't update edit history at the end
)
),
// Tab / Shift + Tab add or remove two spaces on selected lines
"Tab" == K && (
P(),
X(J(R.map((l, i) => i > Y && i < Z ? I ? l.replace(/^ {1,2}/,"") : " " + l : l))),
O(G(Y + 1), G(Z) - 1)
),
//"()[]{}<>" enclose parenthesis
k = M.indexOf(K)) % 2 == 0 && (
P(),
X(V.slice(0, A) + K + V.slice(A, B) + M[k + 1] + V.slice(B)),
O(A + 1, B + 1)
),
// update edit history
V != W[N] && !u && (W = W.slice(0, N + 1), W[++N] = V),
// show cursor info
p.innerHTML = `<button onclick=d.click(b.className='mhqr'[++h%4])>H</button> `+(h%4==3?"":`Ln:${E.length}, Col:${E.pop().length+1}, ${A == B ? 'Pos:' + B : 'Sel:' + (B - A)}, Len:${V.length}`)
)
</script></body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment