{"id":24866695,"url":"https://github.com/willghatch/emacs-composiphrase-objects","last_synced_at":"2026-01-05T23:39:33.843Z","repository":{"id":275223712,"uuid":"925440458","full_name":"willghatch/emacs-composiphrase-objects","owner":"willghatch","description":null,"archived":false,"fork":false,"pushed_at":"2025-02-28T00:42:08.000Z","size":85,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-28T07:18:17.499Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/willghatch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-31T22:12:08.000Z","updated_at":"2025-02-28T00:42:12.000Z","dependencies_parsed_at":"2025-02-27T23:48:41.951Z","dependency_job_id":null,"html_url":"https://github.com/willghatch/emacs-composiphrase-objects","commit_stats":null,"previous_names":["willghatch/emacs-composiphrase-objects"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willghatch%2Femacs-composiphrase-objects","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willghatch%2Femacs-composiphrase-objects/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willghatch%2Femacs-composiphrase-objects/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willghatch%2Femacs-composiphrase-objects/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willghatch","download_url":"https://codeload.github.com/willghatch/emacs-composiphrase-objects/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245721092,"owners_count":20661520,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-01T01:47:27.015Z","updated_at":"2026-01-05T23:39:28.798Z","avatar_url":"https://github.com/willghatch.png","language":"Emacs Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"# composiphrase-objects\n\nThis repo has a bunch of elisp with “text object” definitions.\nThey could be used stand-alone as useful emacs commands for motions, text selection, transposition, etc.\nBut they also are designed specifically with my [composiphrase](https://github.com/willghatch/emacs-composiphrase) library in mind.\nSee [the demo configuration](https://github.com/willghatch/emacs-composiphrase-demo) that uses these and other libraries to build a complete text-object-focused, composable, modal editor with an order of magnitude more operations than Vim, while also making them easier to learn and remember.\n\nThese text objects typically include:\n- Motion forward/backward explicitly to the object's beginning/end.\n- An “expand region” selection function\n- Functions to transpose (drag) the object forward or backward.\n\nThe more interesting of the text objects form trees.\n- These typically respect tree boundaries, making forward/backward motion mean forward/backward among siblings.\n- These come with motions to go up to parent, down to first child, etc.\n- The selection functions can be called repeatedly to expand to parent regions.\n- There are also inner selection functions that select just the children of a tree.\n- Each tree has an in-order traversal function.  This was kinda fun to write, but doesn't seem very useful in practice.\n- Each tree type has an “anchor point”, typically the start of the tree object, that is the main point for operating on the tree.  If the anchor point is not the beginning, movements default to moving to the anchor point instead of beginning/end.\n- Trees should all support tree operations like slurp, barf, promote/unwrap, demote/wrap, transpose-sibling, reorder-ancestors, etc.  In practice I've only implemented some of these on some trees due to time pressure.  I would like to flesh it out over time.\n- Note that while some of the trees may seem like they are specific to specific file types or modes, many are usable to varying degrees outside of their specific mode.  Eg. smartparens is useful outside of lisp, even if not as useful as for lisp.  Indentation trees are useful in virtually any programming language.  Outline trees are typically in org-mode, but there is also outline-minor-mode.  Program source code can be embedded in org-mode.  Etc.  So it is often the case that having multiple different tree views of the same text is useful.  (This is one of the reasons why I believe that pure structured editing, where you don't even view the source tree as text anymore, is going to always be inferior to layering structured editing on top of text.)\n\nNot all objects defined here follow all of these rules.\n\nThis library is not stable.\nThis library does not include any good software engineering practices.\nThis library first accreted slowly as I wrote movements for myself occasionally, typically in a quick and dirty fashion, then rapidly as I decided to implement Composiphrase and related packages.\nI quickly wrote a bunch of movements to have them for myself, and to expand the demo of what Composiphrase can do, in terms of making it much easier to learn and use hundreds of editing commands, by using a composable editing language.\nI wanted to limit the time I would spend futzing with emacs instead of other “more important” projects, so this is mostly slapdash demo-ware.\nThat said, I use it.\nSo feel free to give it a try, just don't expect stability, or for things to work, or to be able to contribute meaningfully (eg. I've written almost no tests!).\n\n# Objects provided\n\n## simple objects\n\nSome objects already exist natively in emacs, but emacs native motions typically move to the end of an object when going forward, and to the beginning of an object when moving backward.\nI want to be able to explicitly move to the beginning or end.\nSo for these objects, I've mostly added those explicit movements, plus selection and transposition functions.\n\n- word\n- sentence\n- paragraph\n- line\n- symbol\n\nIt also includes a hacky, bad, and buggy implementation of a more vi-like word.\nI've been using it for a little while, and I'm undecided as to whether I will keep using it and fix it or switch to using the normal emacs word definition...\n\n## Smartparens (cpo-smartparens)\n\n[Smartparens](https://github.com/Fuco1/smartparens) is an awesome emacs library for structural editing of s-expressions.\nIt is also very similar to [paredit](http://paredit.org/).\nSmartparens has more customizability than paredit, and in particular allows you to define arbitrary delimiters.\nSo I've been a smartparens user for many years.\nThis integration adds:\n\n- Movements explicitly to the beginning/end.\n- Movements that respect tree bounds, so you don't move past the last sibling.\n- Slightly different boundaries for operations.  When point is before an open paren (or before a symbol, or such), operations typically operate on that s-exp that it is before (with some exceptions that operate on the parent when point is on a symbol or other atomic s-exp).  Additionally, when point is immediately after an s-exp (close paren or otherwise), operations operate on the s-exp that is immediately behind point.  When there are sibling s-exps both immediately before and after point (which doesn't happen with typical Lisp style), the sibling after point should be preferred.  IE this implements a consistent anchor point for operations.\n\nBasically it just provides the goodies of smartparens but with some adjustments to standardize with other tree operations in this package.\n\n## Indent Trees (cpo-indent-tree)\n\nTypically when programming, in any language, we use indentation to help us read.\nThis indentation forms trees, where children are indented more than their parents.\nSo `cpo-indent-tree` provides movements to move between siblings, up/down the tree, select indent trees (or the children of an indent tree), transpose indent trees, etc.\nI find this text object to be very useful for working with programming languages that don't otherwise have good editing support, or when other tooling is broken (crashing or something).\nIndent trees work generically and always work.\nThis is one of the earliest objects that I wrote, so I've actually been using this one for a pretty long time, and I've found it pretty valuable.\nIt is still missing several operations, though.\n\n## Outline / org-mode (cpo-outline)\n\nThe headings in outline-mode or org-mode (or outline-minor-mode) form a tree.\nNote that I decided to try to use outline-mode for the implementation, but some things seem broken because of that.\nI may switch to using org-mode-based implementations, which seem more reliable.\nBut I also wanted to use outline-minor-mode, because sometimes I like to use org-mode style heading trees in code comments.\nAnyway, this one is particularly sketchy.\n\n## Treesitter (cpo-treesitter-qd)\n\nThis provides generic treesitter movements.\nThey require a treesitter parser to be initialized in the buffer.\nThis mostly just includes movements, but it also includes `cpo-treesitter-qd-ancestor-reorder` for something like transposing the parent node up the tree (similar to but not quite the same as the smartparens or paredit convolute operation).\nThis all works great for lisp, and much faster than smartparens for the operations supported.\nFor other languages it is... iffy.\n\nIt is difficult for treesitter operations to be truly generic.\nThe first difficulty is figuring out which leaf tree nodes are interesting or uninteresting token types.\nYou can do this OK semi-generically by listing typical programming language keywords and syntax tokens, as the name of the node type for these is typically the literal text of the token.\n\nSome reasons that Lisps allow such great and easy editing functions, such that structured editing functionality was available for lisp many years ago that is still better than anything provided even with smartparens for other languages, are that lisps provide clear “anchor points” due to being fully explicit with s-expression structure, and that s-expressions are extremely regular, so that operations that make sense anywhere in a tree make sense everywhere in a tree.\nWith typical unenlightened language syntax, trees are highly irregular, so you can't “just” slurp/barf tree siblings, etc.\nAnd there is not necessarily any “anchor point”, where you can say that a position within the text corresponds specifically to one and only one tree node in the concrete syntax tree.\n\nSo this quick-and-dirty implementation assumes that the left-most non-interesting leaf node that a non-leaf node has is its anchor point.\nSo for infix math operators, the operator is the anchor point.\n(It's not entirely fair to say that the operator is uninteresting, but do you want to say that the operator corresponds to an identifier-like node for the operator, or to be the one and only anchor point for the infix operation?)\n\nAnyway, I've played with it a little.\nIt works ok, sometimes.\nBy which, I mean it is awesome for Lisp, and it works in some positions for eg. Javascript, but also lets me down in places where I want to use it.\n\nIn the future I will probably also try to write some language-specific treesitter wrappers that work better.\nBut that is a lot of extra work for each of these un-fun syntaxes.\nCome on, people, learn lisp and make them popular.\nLisp syntax is simply better in almost all situations.\nI hated Lisp the first time I used it in college for a class with no guidance on how to write it.\nBut it only takes a few minutes to learn the indentation rules for writing lisp, and then you realize that it is easy to read as long as it is formatted properly.\nAnd the editing!\nLisp is so much more fun when text editing!\nAlso macros (particularly with Racket's macro system!) are awesome, and all of you non-lispers are missing out.\n\nBut I digress.\n\n## cpo-search-movements\n\nOk, this one doesn't really fit, it's not a text object at all, in the sense that the others are.\n\nThis has some wrappers to make emacs isearch behave more like searching in vim.\nIE searching moves specifically to the start of the search pattern, unless you use the function that makes it go explicitly to the end, whether you go forward or backward.\n\nAlso it has `cpo-find-char-beginning-in-line-forward` and friends, which are kinda like (but not quite like) Vim's f/t keys.\nI am an ex-Vim user, ex-evil-mode user, writing “a new modal text editor” in emacs.\nI like this operation, so I wrote it to keep using it.\n\n## future\n\nI intend to write more text object implementations, especially for more trees, especially for trees that already have some emacs library that supports them.\nI am confident that I will add XML at some point with nxml, for example.\n\n## TODO\n\nI need to actually document things here.\nThis readme just gives some vague high-level understanding of the basic ideas of the various text objects.\nIt doesn't list any APIs.\nBut there are a lot of them.\nSee the full composiphrase demo configuration to see an example where all of the motions are bound.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillghatch%2Femacs-composiphrase-objects","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillghatch%2Femacs-composiphrase-objects","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillghatch%2Femacs-composiphrase-objects/lists"}