Most of us learn Emacs one motion at a time – C-f for a character, M-ffor a word, C-n for a line. Useful, but those commands don’t know anythingabout the structure of your code. Emacs has a whole other family of commandsthat operate on balanced expressions and definitions instead, and once theybecome muscle memory they’re hard to give up.Lisp hackers know these commands intimately – they’re the foundationparedit builds on. What’s less appreciated is that theywork in plenty of other languages too. I’ve been leaning on them heavily whilebuilding neocaml, my tree-sitter packagefor OCaml programming, so I’ll use OCaml for the examples here. The commands themselves aregeneral, though, and most of what follows applies to any structure-aware majormode.Sexps and defunsTwo terms show up everywhere in this corner of Emacs, and both are inheritedfrom its Lisp roots: A sexp (“symbolic expression”) is abalanced expression:a literal, anidentifier, a parenthesized group, a list, a function application – whateverthe major mode considers one self-contained unit. A defun is a top-level definition. In Lisp that’s a defun; in OCaml it’sa let binding, a type definition, a module, and so on.The names are Lispy, but the concepts are general, and that’s the whole point.Every command below is built on one of these two notions.Moving aroundThese are the everyday workhorses. In the examples, █ marks point (thecursor). Keybinding Command What it does C-M-f forward-sexp Move forward over a balanced expression C-M-b backward-sexp Move backward over a balanced expression C-M-d down-list Move into a bracketed group C-M-u backward-up-list Move out of the enclosing group C-M-a beginning-of-defun Jump to the start of the current definition C-M-e end-of-defun Jump to the end of the current definition The interesting thing about forward-sexp is that it moves over the largestexpression starting at point. At the head of a function application, that’s thewhole call:let r = █List.map (fun x -> x + 1) numbersAfter C-M-f:let r = List.map (fun x -> x + 1) numbers█But inside a collection it steps over one element at a time, which is exactlywhat you want when you’re editing the elements:let xs = [ █aa; bb; cc ]let xs = [ aa█; bb; cc ]down-list and backward-up-list are a great pair for repositioning. Thelatter is especially nice in a structure-aware mode because it understandskeyword-delimited blocks, not just parens. From deep inside a struct body,C-M-u climbs out to the enclosing module:module M = struct let x = 1 let y = █2endmodule M = █struct let x = 1 let y = 2endEditing with structureMovement is only half the story. The same structural knowledge powers a set ofediting commands that are far more precise than their line- or character-basedcousins. Keybinding Command What it does C-M-SPC mark-sexp Mark the next balanced expression C-M-k kill-sexp Kill the expression after point C-M-t transpose-sexps Swap the two expressions around point M-x delete-pair delete-pair Remove a matched delimiter pair M-x raise-sexp raise-sexp Replace the enclosing expression with the one at point kill-sexp removes a whole expression without you having to hunt for where itends:let xs = █(List.rev ys) @@ tllet xs = █ @@ tltranspose-sexps swaps the expressions on either side of point – a one-keystrokeway to fix arguments you passed in the wrong order:let path = Filename.concat dir █filelet path = Filename.concat file dir█(There’s much more to say about transposition – I covered the whole family inTranspose All The Things.)delete-pair unwraps a bracketed group: put point on the opening delimiter andit deletes both it and its match.let area = pi *. █(r *. r)let area = pi *. █r *. rdelete-pair has a subtle dependency on structure that I ran into head-on whileworking on neocaml – it relies on the mode’s notion of a sexp to find thematching delimiter. I wrote that adventure up separately inRemoving Paired Delimiters in Emacs.Finally, raise-sexp replaces the enclosing expression with thesub-expression at point. It’s the fastest way I know to strip a wrapper – here,dropping a Some around a call:let result = process (Some █(find_opt key tbl))let result = process █(find_opt key tbl)One caveat: raise-sexp needs something to raise into. Invoke it at the toplevel of a binding, where nothing surrounds the expression, and it’ll complainabout unbalanced parentheses. Reach for it inside a call, a list, or aparenthesized expression.The same key, different behaviorHere’s the part that trips people up: these commands behave differently fromone major mode to the next, because each mode gets to define what “anexpression” or “a definition” actually means. They do it through a fewbuffer-local hooks – chiefly forward-sexp-function (which backs C-M-f/C-M-band everything built on them, including kill-sexp, transpose-sexps, anddelete-pair) and beginning-of-defun-function / end-of-defun-function fordefun motion. What plugs into those hooks has changed a lot over the years.The classic backing: syntax tables and regexpsFor most of Emacs’s history, structural commands rested on thesyntax table –the per-mode table that classifies each character as an open or close delimiter,a string quote, a word or symbol constituent, and so on.scan-sexps and scan-listswalk the buffer counting delimiters according to that table, andthat’s what forward-sexp falls back on when a mode sets nothing special. It’swonderfully cheap and works in any buffer, even plain text – but it’s purelylexical. It counts brackets and respects quoting, yet has no idea what afunction or a block actually is. That’s why vanilla backward-up-list can’tclimb out of a begin ... end or OCaml struct ... end block: those aren’tbracket characters, so as far as the syntax table is concerned they don’t exist.Modes that wanted more had two classic options: SMIE(the Simple Minded Indentation Engine). Modes like ruby-mode andoctave-mode feed SMIE a small operator-precedence grammar, and smie-setuppoints forward-sexp-function at smie-forward-sexp-command. Suddenly C-M-funderstands keyword-delimited blocks like if ... end, not just parens. Regexps for definitions. A beginning-of-defun-function that scans for aheader pattern, and an imenu-generic-expression for the imenu index. Quick towrite, but heuristic – easily fooled by code inside strings and comments, or byunconventional formatting.The tree-sitter backing: a real parse treeTree-sitter modes answer the same questions from an actualconcrete syntax tree.forward-sexp-function becomes treesit-forward-sexp; on Emacs 30+, a modedeclares named “things” – sexp, list, sentence, defun, text – viatreesit-thing-settings, and the navigation commands consult them. Defun motiongoes through treesit-beginning-of-defun (driven by treesit-defun-type-regexp),and imenu through treesit-simple-imenu-settings.Because it’s the real grammar rather than a delimiter count, it’s accurateexactly where the lexical approach has to guess. That’s why, earlier, C-M-fcould treat the whole List.map ... application as one node, and C-M-u couldclimb out of a paren-less struct block – and why a stray brace inside a stringnever throws it off. The price of admission is a compiled grammar and areasonably recent Emacs.1This layering is also why a command like delete-pair can quietly misbehave: itleans on the mode’s forward-sexp-function to find the matching delimiter, so ifthat disagrees with the buffer’s syntax table you get surprising deletions.Getting these foundations right is a big part of what makes a structure-awaremajor mode feel solid.What about paredit?As mentioned in the beginning of this articles, all of this probably reminds(some of) you of paredit – and it should. paredit takesthe same structural ideas and turns them up to eleven, adding slurping, barfing,splicing, and the famous guarantee that your parens always stay balanced. Thebuilt-in commands here are the humbler, mode-agnostic ancestors of those ideas.For non-Lisp languages there are spiritual successors built on tree-sitter.Combobulate is the most ambitious –a full structural editing and navigation package for tree-sitter modes, withdrag, splice, and “move by sibling” commands. punitakes a different tack, offering paredit-style soft deletion that works acrossmany languages by leaning on forward-sexp-function. (If you do mix pareditwith other tools, mind the keybindings – I wrote about some common clashes inParedit Keybinding Conflicts.)More than movement and editingOnce a mode knows how to find sexps and defuns, a surprising number of otherfeatures come along for the ride – all powered by the same structural framework: narrow-to-defun (C-x n d) – narrow the buffer to just the currentdefinition. Wonderful for focusing on one function at a time, and it uses theexact same defun detection as C-M-a/C-M-e. which-function-mode – show the name of the definition point is in, inthe mode line. It needs the mode to know where definitions begin and end. imenu –jump to a definition by name. Same structural underpinning. Folding – outline-minor-mode, hs-minor-mode, and the tree-sitteroutline support on Emacs 30+ all fold along structural boundaries. Expanding the region – repeatedly hitting C-M-SPC grows the selectionby sexp, and packages like expreggeneralize that to grow by structural node.Wrapping upThe C-M--prefixed commands are some of the best returns on investment in allof Emacs. They’re not Lisp-only, they’re not tree-sitter-only, and they quietlyget smarter as major modes teach Emacs about their syntax. Learn the handful inthe tables above – C-M-f/C-M-b, C-M-u/C-M-d, C-M-SPC, C-M-k,C-M-a/C-M-e – and you’ll feel the difference in every language you touch.And if a couple of these (delete-pair, raise-sexp) feel essential but lackdefault bindings, give them keys in your major mode of choice. Your future self,mid-refactor, will thank you.That’s all I have for you today. Keep hacking! Tree-sitter support was introduced in Emacs 29, but it got significantly better in Emacs 30. ↩