HN user

qazxcvbnm

277 karma
Posts3
Comments167
View on HN

Finally found a working graphical Emacs browser! I've been looking for just this since webkit widgets died. In my experience on macOS, EAF was very buggy (plus spawning an ugly extra Python window).

This comes with a lovely transient menu even!

Installation was quite smooth, works just as advertised. Still have some hiccups (link hinting often requires multiple calls to detect; events get stuck after pressing Google AI Search popup; on first setup displays what appears to be an undismissable chromium video cast popup; consumes more CPU than ideal), but altogether something I definitely can work with!

The new Emacs features sound great! (We have native window management finally)

I wish we would someday be able to edit in xref too, wgrep having landed in Emacs 30 (especially since project.el grep goes to xref by default).

By the way, anyone more informed know about any work on getting a graphical browser to work on latest Emacs, now that webkit xwidgets is dead for Emacs 30+? (Have tried EAF; extremely buggy on Mac)

M-n, M-p. M-r for searching on past commands. C-c c-n/C-p for next/previous prompt. C-c C-r to scroll to focus on output.

Having switched recently to Emacs, my current issue is how to get Emacs shell history saved properly for my other shell buffers, and getting completions from shell (not Emacs) to work, planning to try MisTTY soon.

What are some more information on the state of the art in runtime adaptation? I confess I do not feel like I possess such a thing from the databases I regularly use.

Adaptation sounds very compelling; if instead of emitting a plan based on a cardinality estimate, we emit a plan and a range of reasonable intermediate cardinalities together with expected time, and interrupt the original plan when the expectations are exceeded by an order of magnitude, and perform alternative plans based on newly gathered physical information, it sounds like it would be greatly beneficial. Are there concrete reasons that this has not been done (e.g. cost, complexity)?

The Synology End Game 11 months ago

When things aren’t ticking perfectly for Synology their software can be kind of weird. Sometimes after power failures, some disks get corrupted and… you simply can’t log in to the Synology UI during this time (unless you “synobootseq --set-boot-done”, why, of course) for an unspecified number of hours.

Their custom software has its quirks (eg scp doesn’t work unless you apply the -O flag, for “security” reasons), and the quirks change sometimes after updates.

I’ve once attempted to implement a solution where ids are generated by UUIDv5 from a certain owner and the relationship of the new item to the owner; that way, users cannot generate arbitrary ids but can still predict ahead of time their new ids to ease optimistic behaviour.

For those who are not aware, knowing how to count the number of structures is (nearly) the same as knowing how to encode said structure as an integer (space-optimally) (the naive general algorithm would not have good time complexity); to convert to integer, simply define some ordering on said structures, enumerate until you find your structure, the index of the structure is the integer encoding; conversely, to decode, simply list all structures and pick the structure with the index of the integer.

A somewhat more “complete” solution that doesn’t give you hints (thus doesn’t rely on the plugin support for all of vim’s vast functionalities), but conditions your instincts to get better: increase the latency of my whole terminal (c.f. https://unix.stackexchange.com/questions/778196/how-to-add-d...) (also see the comment) by running my terminal session on a ssh session into my own machine through a ProxyCommand of the command delay.

Halting (problem) is undecidable. Undecidability is not just the halting problem. Whether you go to the park on a given day is undecidable (assuming you have free will, and all such assumptions), but that you can be an oracle for this does not solve the halting problem.

To be fair, searching with Spotlight has been equally slow and useless for me… Whenever I need to find a file and mistakenly use Command F in my Finder, the complete cessation of activity that inevitably results reminds me yet once again to just go to my terminal to use trusty GNU’s find instead.

You may be interested in my other comment. To put it simply, pure languages remove serialisation of false data dependencies, exposing data parallelism, and interaction nets remove serialisation of false control dependencies, exposing control parallelism.

Both lambda calculus and interaction nets are confluent. That is, for a term that can be normalised (i.e. evaluated), one can obtain the same answer by performing any available action in any order (n.b. if the chosen order terminates). For example, for `A (B) (C (D))`, I can choose to first evaluate either `A (B)` or `C (D)` and the final answer will be the same. This is true in both systems (although the reduction in interaction nets has more satisfactory properties).

The key reason one may consider interaction nets to be more parallelisable than lambda calculus is that the key evaluation operation is global in lambda calculus, but local in interaction nets. The key evaluation operation in labmda calculus is (beta) reduction. For instance, if one evaluates a lambda term `(\n -> f n n) x`, reduction takes this to the term `f x x`. To do so, one must duplicate the entire term `x` from the argument to perform the computation, by either 1) physical duplication, or 2) keeping track of references. Both are unsatisfactory solutions with many properties hindering parallelism. As I shall explain, the term `x` may be of unbounded size or be intertwined non-locally with a large part of the control graphs of other terms.

If `x` is simply a ground term (i.e. a piece of data), then it seems like either duplication or keeping track of the references would be an inevitable and reasonable cost, with the usual managed-language issues of garbage collection. If one decides to solve the problem by attempting to force the argument to be a ground term, one would find the only method to be to impose eager evaluation, evaluating terms by always first evaluating the leaf of the expression, before evaluating internal nodes in the expression. Eager evaluation can easily become unboundedly wasteful when one strives to reuse a general computation for some more specific use cases, so one may not prefer an eager evaluation doctrine.

However, once one evaluates in an order that is not strictly eager (e.g. lazy evaluation), the terms that one is duplicating or referencing are no longer simple pieces of data, but pieces of a (not necessarily acyclic) control graph, and any referencing logic quickly becomes very complicated. Moreover, the argument `x` could also be a function, and keeping track of references would involve keeping track of closures over different variables and scopes, which complicates the problem of sharing even further.

Thus, either one follows an eager evaluation order, in which most of the nodes in a term's expression tree are not available for evaluation yet, and available pieces of work for evaluation are only generated as evaluation happens, which imposes a global and somewhat strict serialised order of execution, or one deals with a big complicated shared graph, which is also inconvenient to be distributed across computational resources.

In contrast with lambda calculus, the key evaluation operation in interaction nets is local. Interaction nets can be seen as more low-level than lambda calculus, and both code and data are represented as graphs of nodes to be evaluated. Thus, a large term is represented as a large net, and regardless of the unbounded size of a term, in one unit of evaluation, only one node from the term's graph is involved.

Given a graph of some 'net' to be evaluated, one can choose any "active" node and begin evaluating right there, and the result of computation in that unit of evaluation will be guaranteed to affect only the nodes originally connected to the evaluated node, no referencing involved. Thus, the problem of computation becomes almost embarrassingly parallel, where workers simply pick any piece of a graph and locally add or remove from that piece of the graph.

This is what is meant when one refers to interaction nets being more parallelisable than lambda calculus.

Tiny Pointers 1 year ago

I read the paper a few years ago and I agree that for such an incredible algorithmic improvement, it’s not trivial to find a use case, as you still need to maintain a separate (albeit algorithmically insignificant) lookup table. When I read the paper I (mistakenly) hoped it could be used for indexing on-disk structures without ever hitting the disk for things like B tree internal nodes. To get its sweet sweet algorithmic complexity, up its sleeve is once again (as I recall) the age old trick of rebuilding the structure when the size doubles, which makes it much less efficient than it sounds for most practical use cases. I suppose one good use case for this might be compressing database indexes, where you need to maintain a separate structure anyway and the space savings can be worth it.

The example is meant to demonstrate that whether I say 平 as ping or peng changes the meaning of the sentence. It does not depend on how I feel like it.

Another example: I buy some raisins, and the lady gives me some. I say: 咁多! If I pronounce "gam3do1", I say "That's so much!". If I pronounce "gam3doe1", I say "That's so little!".

(Yes, contrived. Almost always the former is used. But both can be.)

Yes, I have mainly put awkward usages in the latter section, which will hopefully be useful to the author. If you compare the paragraphs, the outright grammatical problems I simply fixed. The author’s language is definitely comprehensible to a patient listener, but it can be much better still.

I cannot corroborate the experience of the other commenter. 念 is very definitively nim6, with lim6 as a common variant/lazy pronunciation. It is not at all uncommon that words have a variant pronunciation when used in certain contexts (e.g. as jargon or slang), such as Wing Chun in your case, but the word itself is certainly not properly or even commonly pronounced the "lazy" way otherwise (c.f. 執念 zap1nim6; 萬念俱灰 maan6nim6keoi1fui1; 惡念 ok3nim6).

There are many cases where variant pronunciations sound natural, but (individually) "correct" pronunciations sound unnatural (e.g. 土瓜灣 tou2gwaa1waan1 "correct" but unnatural, tou2gwaa1waan4 "variant" and natural). I have never encountered a case where a correct pronunciation would be unnatural where "lazy" pronunciations would be natural.

On another note, your Cantonese sounds a bit unnatural (without many of the connecting words between thoughts) (typical for learners). A more natural phrasing might be something like this: 我打詠春嘅。學院(?)入面我師父同我哋都係讀lim嘅。反而如果有人讀nim會好奇怪。我想你明白粵卷唔使百分百啱晒嘅。講嘢可能淨係使啱八成到九成,大部份人都明你講嘅嘢。咁都OK嘅。學廣東話嘅學生可以多啲地方繼續學廣東話就可以越來越好。

A few notes on your usage:

"我都打詠春" 都 here seems unnecessary, as you have not discussed 詠春 previously.

"學院" slightly dubious, usually used for more academic purposes. For martial arts, 館 or 武館 may be more typical, but I suppose it is possible that your school brands itself as 學院.

"我師傅" "師傅" and "師父" sound the same, but mean quite different things. 師傅 is a polite name for someone working in some field, but 師父 is your master. (In fact this is another example of "lazy" or variant pronunciations, which is emphatically not always the correct pronunciation; 父 here is fu2, but by itself is definitively fu6)

"都話"lim"" "話" probably refers more to the topic of what someone says rather than the pronunciation, which the word "讀" would make clear, that we discuss pronunciation

"我推介你試吓明粵卷唔使係一百%啱" too be honest I'm not completely sure what this sentence means. I hope what I put was what you intended.

"一百%" 百分百 is more natural

"八十到九十%" 八成到九成 (lit. 8 10%s to 9 10%s) is more natural

"明你講乜嘢" (sounds like: understand what on earth you're saying) "乜嘢" emphasises the object like a question (i.e. sounds like a rhetorical question; when not a question, usually would be used when expressing frustration/complaining/scolding); no need to emphasise, "明你講嘅嘢" would be fine.

"學生可以繼續學廣東話。越來越好。" disjointed sentences, not completely sure your intended meaning. I hope what I put was what you intended.

As a native speaker I assure you that 請 is pronounced differently in context, and that both readings are perfectly Cantonese.

For a more clear example, see 平: 大平賣 ("vernacular" reading; peng4) (lit. big cheap sale; i.e. sale) vs 平面 ("literary" reading; ping4) (lit. flat surface; i.e. surface). peng4 is the "vernacular" reading but used exclusively for meaning cheap. ping4 is "literary" but used everywhere else. "vernacular" versus "literary" is a linguistic classification, but do not necessarily represent either being more common than the other reading. Both readings exist.

The State of Vim 1 year ago

When you get to using [count]g[something] and g[count][something] and <C-@>[count]g[something] etc a lot, you start getting a bit confused about where you need to put the count to make things work. So you can make putting the count anywhere work; now, don't worry and just start pressing g or <C-@>g for window actions, and put a count if you realise you need it:

    map <expr> <C-@>1 Termcountmap(1, "")
    map <expr> <C-@>2 Termcountmap(2, "")
    map <expr> <C-@>3 Termcountmap(3, "")
    map <expr> <C-@>4 Termcountmap(4, "")
    map <expr> <C-@>5 Termcountmap(5, "")
    map <expr> <C-@>6 Termcountmap(6, "")
    map <expr> <C-@>7 Termcountmap(7, "")
    map <expr> <C-@>8 Termcountmap(8, "")
    map <expr> <C-@>9 Termcountmap(9, "")
    map! <expr> <C-@>1 Termcountmap(1, '<Esc>')
    map! <expr> <C-@>2 Termcountmap(2, '<Esc>')
    map! <expr> <C-@>3 Termcountmap(3, '<Esc>')
    map! <expr> <C-@>4 Termcountmap(4, '<Esc>')
    map! <expr> <C-@>5 Termcountmap(5, '<Esc>')
    map! <expr> <C-@>6 Termcountmap(6, '<Esc>')
    map! <expr> <C-@>7 Termcountmap(7, '<Esc>')
    map! <expr> <C-@>8 Termcountmap(8, '<Esc>')
    map! <expr> <C-@>9 Termcountmap(9, '<Esc>')
    tmap <expr> <C-@>1 Termcountmap(1, '<C-@>N')
    tmap <expr> <C-@>2 Termcountmap(2, '<C-@>N')
    tmap <expr> <C-@>3 Termcountmap(3, '<C-@>N')
    tmap <expr> <C-@>4 Termcountmap(4, '<C-@>N')
    tmap <expr> <C-@>5 Termcountmap(5, '<C-@>N')
    tmap <expr> <C-@>6 Termcountmap(6, '<C-@>N')
    tmap <expr> <C-@>7 Termcountmap(7, '<C-@>N')
    tmap <expr> <C-@>8 Termcountmap(8, '<C-@>N')
    tmap <expr> <C-@>9 Termcountmap(9, '<C-@>N')

    function! Termcountmap(initcount, normalkeys)
     let termcount = a:initcount
     while 1
      try
       let char = getchar()
      catch /^Vim:Interrupt$/
       return ""
       endtry
    
      if type(char) == 0
       let char = nr2char(char)
       endif
    
      if char == '0'
       let termcount = termcount * 10
      elseif char == '1'
       let termcount = termcount * 10 + 1
      elseif char == '2'
       let termcount = termcount * 10 + 2
      elseif char == '3'
       let termcount = termcount * 10 + 3
      elseif char == '4'
       let termcount = termcount * 10 + 4
      elseif char == '5'
       let termcount = termcount * 10 + 5
      elseif char == '6'
       let termcount = termcount * 10 + 6
      elseif char == '7'
       let termcount = termcount * 10 + 7
      elseif char == '8'
       let termcount = termcount * 10 + 8
      elseif char == '9'
       let termcount = termcount * 10 + 9
      elseif char == 'g'
       return a:normalkeys .. termcount .. 'g'
      elseif char == "\<C-g>"
       return a:normalkeys .. termcount .. "\<C-@>\<C-g>"
      elseif char == 'w'
       return a:normalkeys .. termcount .. "\<C-@>w"
      elseif char == 'W'
       return a:normalkeys .. termcount .. "\<C-@>W"
      elseif char == 't'
       return a:normalkeys .. termcount .. "\<C-@>t"
      elseif char == 'b'
       return a:normalkeys .. termcount .. "\<C-@>b"
      else
       return a:normalkeys .. ":\<C-u>" .. termcount .. ' wincmd ' .. char .. "\<CR>\<C-l>"
       endif
    
      endwhile
     endfunction
    
    map <expr> g<C-@> <C-@>g
    
    map <expr> g1 Prefixcountmap (v:count * 10 + 1, "", 'g')
    map <expr> g2 Prefixcountmap (v:count * 10 + 2, "", 'g')
    map <expr> g3 Prefixcountmap (v:count * 10 + 3, "", 'g')
    map <expr> g4 Prefixcountmap (v:count * 10 + 4, "", 'g')
    map <expr> g5 Prefixcountmap (v:count * 10 + 5, "", 'g')
    map <expr> g6 Prefixcountmap (v:count * 10 + 6, "", 'g')
    map <expr> g7 Prefixcountmap (v:count * 10 + 7, "", 'g')
    map <expr> g8 Prefixcountmap (v:count * 10 + 8, "", 'g')
    map <expr> g9 Prefixcountmap (v:count * 10 + 9, "", 'g')
    tmap <expr> <c-@>g1 Prefixcountmap (v:count * 10 + 1, '<c-@>n', 'g')
    tmap <expr> <c-@>g2 Prefixcountmap (v:count * 10 + 2, '<c-@>n', 'g')
    tmap <expr> <c-@>g3 Prefixcountmap (v:count * 10 + 3, '<c-@>n', 'g')
    tmap <expr> <c-@>g4 Prefixcountmap (v:count * 10 + 4, '<c-@>n', 'g')
    tmap <expr> <c-@>g5 Prefixcountmap (v:count * 10 + 5, '<c-@>n', 'g')
    tmap <expr> <c-@>g6 Prefixcountmap (v:count * 10 + 6, '<c-@>n', 'g')
    tmap <expr> <c-@>g7 Prefixcountmap (v:count * 10 + 7, '<c-@>n', 'g')
    tmap <expr> <c-@>g8 Prefixcountmap (v:count * 10 + 8, '<c-@>n', 'g')
    tmap <expr> <c-@>g9 Prefixcountmap (v:count * 10 + 9, '<c-@>n', 'g')
    
    map <expr> <C-@><C-g>1 Prefixcountmap (v:count * 10 + 1, "", '<C-@><C-g>')
    map <expr> <C-@><C-g>2 Prefixcountmap (v:count * 10 + 2, "", '<C-@><C-g>')
    map <expr> <C-@><C-g>3 Prefixcountmap (v:count * 10 + 3, "", '<C-@><C-g>')
    map <expr> <C-@><C-g>4 Prefixcountmap (v:count * 10 + 4, "", '<C-@><C-g>')
    map <expr> <C-@><C-g>5 Prefixcountmap (v:count * 10 + 5, "", '<C-@><C-g>')
    map <expr> <C-@><C-g>6 Prefixcountmap (v:count * 10 + 6, "", '<C-@><C-g>')
    map <expr> <C-@><C-g>7 Prefixcountmap (v:count * 10 + 7, "", '<C-@><C-g>')
    map <expr> <C-@><C-g>8 Prefixcountmap (v:count * 10 + 8, "", '<C-@><C-g>')
    map <expr> <C-@><C-g>9 Prefixcountmap (v:count * 10 + 9, "", '<C-@><C-g>')
    map! <expr> <C-@><C-g>1 Prefixcountmap (v:count * 10 + 1, '<Esc>', '<C-@><C-g>')
    map! <expr> <C-@><C-g>2 Prefixcountmap (v:count * 10 + 2, '<Esc>', '<C-@><C-g>')
    map! <expr> <C-@><C-g>3 Prefixcountmap (v:count * 10 + 3, '<Esc>', '<C-@><C-g>')
    map! <expr> <C-@><C-g>4 Prefixcountmap (v:count * 10 + 4, '<Esc>', '<C-@><C-g>')
    map! <expr> <C-@><C-g>5 Prefixcountmap (v:count * 10 + 5, '<Esc>', '<C-@><C-g>')
    map! <expr> <C-@><C-g>6 Prefixcountmap (v:count * 10 + 6, '<Esc>', '<C-@><C-g>')
    map! <expr> <C-@><C-g>7 Prefixcountmap (v:count * 10 + 7, '<Esc>', '<C-@><C-g>')
    map! <expr> <C-@><C-g>8 Prefixcountmap (v:count * 10 + 8, '<Esc>', '<C-@><C-g>')
    map! <expr> <C-@><C-g>9 Prefixcountmap (v:count * 10 + 9, '<Esc>', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>1 Prefixcountmap (v:count * 10 + 1, '<C-@>N', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>2 Prefixcountmap (v:count * 10 + 2, '<C-@>N', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>3 Prefixcountmap (v:count * 10 + 3, '<C-@>N', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>4 Prefixcountmap (v:count * 10 + 4, '<C-@>N', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>5 Prefixcountmap (v:count * 10 + 5, '<C-@>N', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>6 Prefixcountmap (v:count * 10 + 6, '<C-@>N', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>7 Prefixcountmap (v:count * 10 + 7, '<C-@>N', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>8 Prefixcountmap (v:count * 10 + 8, '<C-@>N', '<C-@><C-g>')
    tmap <expr> <C-@><C-g>9 Prefixcountmap (v:count * 10 + 9, '<C-@>N', '<C-@><C-g>')
    
    function! Prefixcountmap(initcount, prefix, countprefix)
     let termcount = a:initcount
     while 1
      try
       let char = getchar()
      catch /^Vim:Interrupt$/
       return ""
       endtry
    
      if type(char) == 0
       let char = nr2char(char)
       endif
    
      if char == '0'
       let termcount = termcount * 10
      elseif char == '1'
       let termcount = termcount * 10 + 1
      elseif char == '2'
       let termcount = termcount * 10 + 2
      elseif char == '3'
       let termcount = termcount * 10 + 3
      elseif char == '4'
       let termcount = termcount * 10 + 4
      elseif char == '5'
       let termcount = termcount * 10 + 5
      elseif char == '6'
       let termcount = termcount * 10 + 6
      elseif char == '7'
       let termcount = termcount * 10 + 7
      elseif char == '8'
       let termcount = termcount * 10 + 8
      elseif char == '9'
       let termcount = termcount * 10 + 9
      else
       return a:prefix .. termcount .. a:countprefix .. char
       endif
    
      endwhile
     endfunction
The State of Vim 1 year ago

Some other things I do:

Invoke terminals on directories (instead of netrw). So, I can e.g. sp. to split to a new terminal.

  function! Isdir(dir) abort
   return !empty(a:dir) && (isdirectory(a:dir) ||
    \ (!empty($SYSTEMDRIVE) && isdirectory('/'..tolower($SYSTEMDRIVE[0])..a:dir)))
   endfunction
  
  augroup terminal-explorer
   au!
   au VimEnter * sil! au! FileExplorer *
   au VimEnter * sil! au! Network *
   au VimEnter * sil! au! AuNetrwEvent *
   " au FileType netrw cd % | bw | exe 'terminal ++curwin'
   au BufEnter * if &filetype != 'netrw' && Isdir(expand('%')) | lcd % | exe bufnr("%") ..'bufdo terminal ++curwin' | endif
   augroup end
Make <C-@>m close windows instead:
    noremap <C-@>m <C-w>c
    noremap! <C-@>m <Esc><C-w>c
    tnoremap <C-@>m <C-@>c
Make <C-@>p find the next window if there is no previous window:
    noremap <expr> <silent> <C-@>p ':<C-u>wincmd p <Bar> if win_getid () == ' .. win_getid () .. ' <Bar> wincmd w <Bar> endif <CR>'
    noremap! <expr> <silent> <C-@>p '<Esc>:<C-u>wincmd p <Bar> if win_getid () == ' .. win_getid () .. ' <Bar> wincmd w <Bar> endif <CR>'
    tnoremap <expr> <silent> <C-@>p '<C-@>:<C-u>wincmd p <Bar> if win_getid () == ' .. win_getid () .. ' <Bar> wincmd w <Bar> endif <CR>'
Use <C-@>~. to detach the GNU screen that my vim is running in (I put my vim in a screen with `unbindall` i.e. I use screen merely for keeping my vim alive) (<C-@>~. is in analogy to the SSH quit keybinding <CR>~.).
    noremap <expr> <silent> <C-@>~. ':<C-u>silent !screen -X detach<CR>'
    noremap! <expr> <silent> <C-@>~. '<C-@>:<C-u>silent !screen -X detach<CR>'
    tnoremap <expr> <silent> <C-@>~. '<C-@>:<C-u>silent !screen -X detach<CR>'
With gf and gF, opening files just from a terminal grep is practically more convenient than vim's :grep. For gf in a new tab:
    map <silent> <C-@><C-f> :<C-u>tab split<CR>gf
With terminal multiplexing in vim, you start getting a lot of buffers. You can consider getting a custom tabline. Other tab management features become more important too. You might expect [count]gt to behave just like gt for [count] times:
    nnoremap <expr> gt '@_' .. ((v:count1 + (tabpagenr () - 1)) % (tabpagenr('$') ) + 1) .. 'gt'
For ease of moving to the last tab:
    nnoremap <expr> g<C-t> '@_' .. (((v:count == 0) ? tabpagenr ('$') : v:count)) .. 'gt'
To consistently access the gt family of bindings regardless of mode:
    map <C-@>gt gt
    map <C-@>gT gT
    map <C-@>g<C-t> g<C-t>
    map! <C-@>gt <Esc>gt
    map! <C-@>gT <Esc>gT
    map! <C-@>g<C-t> <Esc>g<C-t>
    tmap <C-@>gt <C-@>Ngt
    tmap <C-@>gT <C-@>NgT
    tmap <C-@>g<C-t> <C-@>Ng<C-t>
When you use <C-w>T a lot to move your windows in to new tabs, you might sometimes want to move them to the previous tab instead of the next one:
    nnoremap <silent> <C-@><C-t> <C-w>T
    nnoremap <silent> <C-@>T <C-w>T:<C-u>tabm -1<CR>
To access these two in any mode consistently:
    map! <C-@><C-t> <Esc><C-@><C-t>
    map! <C-@>T <Esc><C-@>T
    tmap <C-@><C-t> <C-@>N<C-@><C-t>
    tmap <C-@>T <C-@>N<C-@>T
Analogously to gt and gT, sometimes you may want to move your tabs around, or split them:
    function! SSStabmadjust(tabmnum)
     if a:tabmnum < tabpagenr ()
      return a:tabmnum - 1
     else
      return a:tabmnum
      endif
     endfunction

    noremap <silent> <C-@><C-g>s :<C-u>tab split<CR>
    noremap <expr> <silent> <C-@><C-g>t '@_:<C-u>' .. SSStabmadjust ((v:count1 + (tabpagenr () - 1 )) % (tabpagenr('$') ) + 1) .. 'tabm<CR>'
    noremap <expr> <silent> <C-@><C-g>T '@_:<C-u>' .. SSStabmadjust (((- v:count1 + (tabpagenr () - 1 )) % (tabpagenr('$') ) + tabpagenr('$') ) % (tabpagenr('$') ) + 1) .. 'tabm<CR>'
    noremap <expr> <silent> <C-@><C-g><C-t> ':<C-u>' .. ((v:count == 0) ? "" : SSStabmadjust ((v:count % (tabpagenr ('$') )) )) .. 'tabm<CR>'
    map! <C-@><C-g>s <Esc><C-@><C-g>s
    map! <C-@><C-g>t <Esc><C-@><C-g>t
    map! <C-@><C-g>T <Esc><C-@><C-g>T
    map! <C-@><C-g><C-t> <Esc><C-@><C-g><C-t>
    tmap <C-@><C-g>s <C-@>N<C-@><C-g>s
    tmap <C-@><C-g>t <C-@>N<C-@><C-g>t
    tmap <C-@><C-g>T <C-@>N<C-@><C-g>T
    tmap <C-@><C-g><C-t> <C-@>N<C-@><C-g><C-t>
The State of Vim 1 year ago

vimrc for 4)

    vnoremap <C-@>s <C-w>sgv
    vnoremap <C-@>v <C-w>vgv

    nnoremap <expr> <C-@>w '@_' .. ((v:count1 + (winnr() - 1)) % (winnr('$')) + 1) .. '<C-w>w'
    nnoremap <expr> <C-@>W '@_' .. (((- v:count1 + (winnr() - 1)) % (winnr('$')) + winnr('$')) % (winnr('$')) + 1) .. '<C-w>w'
    nnoremap <expr> <C-@>t '@_' .. ((v:count1 - 1) % (winnr('$')) + 1) .. '<C-w>w'
    nnoremap <expr> <C-@>b '@_' .. (((- v:count1) % (winnr('$')) + winnr('$')) % (winnr('$')) + 1) .. '<C-w>w'
    inoremap <C-@><C-@> <C-@>
    map! <C-@>w <Esc><C-@>w
    map! <C-@>W <Esc><C-@>W
    map! <C-@>t <Esc><C-@>t
    map! <C-@>b <Esc><C-@>b
    tmap <C-@>w <C-@>N<C-@>w
    tmap <C-@>W <C-@>N<C-@>W
    tmap <C-@>t <C-@>N<C-@>t
    tmap <C-@>b <C-@>N<C-@>b