├── .gitignore ├── .travis.yml ├── Cask ├── LICENSE ├── README.org ├── screen.png ├── spaceline-config.el ├── spaceline-segments.el ├── spaceline.el └── tests.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | /.cask 3 | *-autoloads.el 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | dist: trusty 3 | sudo: false 4 | 5 | branches: 6 | only: 7 | - master 8 | - codegen 9 | env: 10 | - EVM_EMACS=emacs-24.4-travis 11 | - EVM_EMACS=emacs-24.5-travis 12 | - EVM_EMACS=emacs-25.1-travis 13 | - EVM_EMACS=emacs-25.2-travis 14 | - EVM_EMACS=emacs-25.3-travis 15 | - EVM_EMACS=emacs-26.1-travis 16 | - EVM_EMACS=emacs-git-snapshot-travis 17 | 18 | matrix: 19 | allow_failtures: 20 | env: 21 | - EVM_EMACS=emacs-git-snapshot-travis 22 | 23 | before_install: 24 | - curl -fsSkL https://gist.github.com/rejeep/ebcd57c3af83b049833b/raw > x.sh && source ./x.sh 25 | - evm install $EVM_EMACS --use --skip 26 | 27 | install: 28 | - cask install 29 | 30 | script: 31 | - cask emacs --batch -l ert -l spaceline.el -l tests.el -f ert-run-tests-batch-and-exit 32 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source gnu) 2 | (source melpa) 3 | 4 | (package-file "spaceline.el") 5 | 6 | (development 7 | (depends-on "f") 8 | (depends-on "ecukes") 9 | (depends-on "ert-runner") 10 | (depends-on "el-mock")) 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | 676 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Spaceline 2 | 3 | [[https://melpa.org/#/spaceline][http://melpa.org/packages/spaceline-badge.svg]] [[https://stable.melpa.org/#/spaceline][https://stable.melpa.org/packages/spaceline-badge.svg]] [[https://travis-ci.org/TheBB/spaceline][https://travis-ci.org/TheBB/spaceline.svg]] [[https://github.com/syl20bnr/spacemacs][file:https://cdn.rawgit.com/syl20bnr/spacemacs/442d025779da2f62fc86c2082703697714db6514/assets/spacemacs-badge.svg]] 4 | 5 | * Contents :TOC_2_gh: 6 | - [[#introduction][Introduction]] 7 | - [[#the-default-themes][The default themes]] 8 | - [[#optional-dependencies][Optional dependencies]] 9 | - [[#troubleshooting][Troubleshooting]] 10 | - [[#moderate-configuration][Moderate configuration]] 11 | - [[#turning-segments-on-and-off][Turning segments on and off]] 12 | - [[#the-highlight-face][The highlight face]] 13 | - [[#other-faces][Other faces]] 14 | - [[#powerline-separators][Powerline separators]] 15 | - [[#hooks][Hooks]] 16 | - [[#unicode-numbers][Unicode numbers]] 17 | - [[#minor-modes-separator][Minor modes separator]] 18 | - [[#org-clock][Org clock]] 19 | - [[#deep-configuration][Deep configuration]] 20 | - [[#segments][Segments]] 21 | - [[#defining-a-segment][Defining a segment]] 22 | - [[#properties][Properties]] 23 | - [[#bindings][Bindings]] 24 | - [[#compiling-a-mode-line][Compiling a mode-line]] 25 | - [[#tweaking-or-defining-your-own-mode-line][Tweaking or defining your own mode-line]] 26 | 27 | * Introduction 28 | This is the package that provides [[http://spacemacs.org/][Spacemacs]] with its famous mode-line theme. It 29 | has been extracted as an independent package for general fun and profit. 30 | 31 | This package provides features for three kinds of users. 32 | 33 | 1. You just want to use the Spacemacs mode-line theme and forget about it. 34 | 2. You want to use something similar to the Spacemacs mode-line theme, but with 35 | a handful of easy tweaks. 36 | 3. You want an easy-to-use library for building your own mode-line from scratch, 37 | and you think the Spacemacs theme looks good. 38 | 39 | The functionality for each are described in the following sections. 40 | 41 | The files in this package are organized as follows. Choose which you want to 42 | load based on what you want to do. 43 | 44 | - =spaceline.el=: Contains the core library used to define segments and render 45 | the modeline. It defines no segments by itself except the =global= segment. 46 | (See below.) 47 | - =spaceline-segments.el=: Defines all the segments used by the default 48 | Spacemacs theme, but doesn’t do anything with them. 49 | - =spaceline-config.el=: Defines the default themes. 50 | 51 | * The default themes 52 | To install it, just load =spaceline-config= and call the theme function you 53 | want. E.g. 54 | 55 | #+BEGIN_SRC emacs-lisp 56 | (require 'spaceline-config) 57 | (spaceline-spacemacs-theme) 58 | #+END_SRC 59 | 60 | The package comes bundled with two themes: 61 | 62 | - =spaceline-spacemacs-theme=: The theme used by Spacemacs 63 | - =spaceline-emacs-theme=: A theme which is similar to the one used by 64 | Spacemacs, but which has been designed to look good without the dependencies 65 | that the Spacemacs theme needs. 66 | 67 | In addition, Spaceline supports custom themes for Info+ and Helm. These can be 68 | enabled through global minor modes: 69 | 70 | - =spaceline-helm-mode= 71 | - =spaceline-info-mode= (requires the [[http://www.emacswiki.org/emacs/InfoPlus][info+]] package) 72 | 73 | These are also defined in =spaceline-config.el=. 74 | 75 | ** Optional dependencies 76 | These themes include several segments that depend on third-party packages. If 77 | these packages are not installed, these segments will be invisible and not show 78 | any output. As such, they can be considered /optional/ dependencies. 79 | 80 | Here follows a brief list of these dependencies. For more information consult 81 | the upstream sources. 82 | 83 | *** Persp-mode 84 | [[https://github.com/Bad-ptr/persp-mode.el][Persp-mode]] is a powerful workspace-like package. Spaceline shows the current 85 | workspace name. 86 | 87 | *** Eyebrowse 88 | [[https://github.com/wasamasa/eyebrowse][Eyebrowse]] is a simpler workspace-like package. If it is installed, The Spacemacs 89 | theme will show the current workspace number. The Emacs theme uses the workspace 90 | number as a fallback for the perspective name: thus if persp-mode is installed, 91 | the Eyebrowse workspace will not be shown. 92 | 93 | *** Winum 94 | [[https://github.com/deb0ch/winum.el][Winum]] shows a number for each window, and it works with both themes. 95 | 96 | To prevent =winum= from inserting its own number in the mode-line, you have to 97 | set =winum-auto-setup-mode-line= to nil before activating =winum-mode=: 98 | 99 | #+BEGIN_SRC emacs-lisp 100 | (setq winum-auto-setup-mode-line nil) 101 | (winum-mode) 102 | #+END_SRC 103 | 104 | *** Auto-compile 105 | [[https://github.com/tarsius/auto-compile][Auto-compile]] automatically compiles Emacs Lisp files on save if there is an 106 | older byte-compiled file. Spaceline shows warnings when they occur. 107 | 108 | *** Anzu 109 | [[https://github.com/syohex/emacs-anzu][Anzu]] shows the current match and the total number of matches while searching. 110 | 111 | Note that Anzu inserts itself in the modeline, to let spaceline handle the 112 | modeline, make sure to =(setq anzu-cons-mode-line-p nil)= or customize it. 113 | 114 | *** Flycheck 115 | [[https://github.com/flycheck/flycheck/][Flycheck]] is a powerful syntax-checking package. Spaceline shows errors, warnings 116 | and notifications from it. 117 | 118 | *** ERC 119 | [[http://www.emacswiki.org/emacs/ERC][ERC]] is an IRC client built in to Emacs. Spaceline shows channels with new 120 | messages if you have =erc-track= turned on. 121 | 122 | *** Org 123 | Spaceline shows the currently clocking [[http://orgmode.org/][org-mode]] task. 124 | 125 | *** Org-pomodoro 126 | Spaceline integrates with [[HTtps://github.com/lolownia/org-pomodoro][org-pomodoro]] by showing its clocks. 127 | 128 | *** Python virtual environments 129 | The currently active environments as reported by [[https://github.com/proofit404/pyenv-mode][pyenv-mode]] or [[https://github.com/jorgenschaefer/pyvenv][pyvenv]] are shown 130 | in Spaceline. 131 | 132 | *** Nyan cat 133 | [[https://github.com/TeMPOraL/nyan-mode][Nyan-mode]] shows the current position in the buffer with kittens and rainbows. 134 | 135 | *** Fancy battery 136 | [[https://github.com/lunaryorn/fancy-battery.el][Fancy-battery]] shows battery information in the modeline. 137 | 138 | *** Evil 139 | [[https://bitbucket.org/lyro/evil/wiki/Home][Evil]] makes Emacs behave like Vim. The first segment in the Spacemacs theme shows 140 | the current Evil state if all the other dependencies do not report information 141 | (i.e. no perspective, workspace or window number). The Emacs theme does not 142 | include any information from Evil. 143 | 144 | You can color the modeline according to the current Evil state by setting 145 | =spaceline-highlight-face-func= to =spaceline-highlight-face-evil-state=. 146 | 147 | ** Troubleshooting 148 | There are a number of reasons why Spaceline might look different on your setup 149 | compared to Spacemacs proper. Some of the most important ones are addressed here. 150 | 151 | - You’re missing an optional dependency. Spacemacs includes packages that 152 | display information in the mode-line. The leftmost segment is invisible if 153 | =eyebrowse-mode=, =persp-mode=, =window-numbering-mode= and =evil= are all not 154 | present. If you don’t wish to use these packages, consider using the Emacs 155 | theme. 156 | 157 | - Consider setting or increasing the value of =powerline-height= to give your 158 | mode-line some room to breathe. 159 | 160 | - The default powerline separator is =arrow=, but Spacemacs uses =wave=. You 161 | should try out various settings of =powerline-default-separator= to find the 162 | one that works for you. Note that you need to recompile the modeline with =M-x 163 | spaceline-compile= after setting this variable. 164 | 165 | - If you’re using =eyebrowse-mode= or =window-numbering-mode=, consider setting 166 | =spaceline-workspace-numbers-unicode= and =spaceline-window-numbers-unicode= 167 | to =t= to get the nice-looking unicode numbers seen in the screenshot. 168 | 169 | - Use [[https://github.com/emacsmirror/diminish][Diminish]] to tweak the output of the minor modes segment. 170 | 171 | - To get the mode-line highlight to change color depending on the evil state, 172 | set =spaceline-highlight-face-func= to =spaceline-highlight-face-evil-state=. 173 | 174 | * Moderate configuration 175 | 176 | ** Turning segments on and off 177 | Each segment has a variable =spaceline-NAME-p= that can switch the segment off 178 | by setting it to =nil=. There are also three convenient interactive functions 179 | for toggling: 180 | 181 | - =spaceline-toggle-= 182 | - =spaceline-toggle--on= 183 | - =spaceline-toggle--off= 184 | 185 | These can be bound to whichever keys you like. 186 | 187 | Here is a complete list of segments bundled with Spacemacs. 188 | 189 | - =persp-name=: integrates with =persp-mode=. 190 | - =workspace-number=: integrates with =eyebrowse=. 191 | - =window-number=: integrates with =window-numbering=. 192 | - =evil-state=: shows the current evil state, integrates with =evil=. 193 | - =anzu=: integrates with =anzu=. 194 | - =auto-compile=: integrates with =auto-compile=. 195 | - =buffer-modified=: the standard marker denoting whether the buffer is modified 196 | or not. 197 | - =buffer-size=: the size of the buffer. 198 | - =buffer-id=: the name of the buffer. 199 | - =remote-host=: the host for remote buffers. 200 | - =major-mode=: the current major mode. 201 | - =flycheck-error=: number of flycheck errors, integrates with =flycheck=. 202 | - =flycheck-warning=: number of flycheck warnings, integrates with =flycheck=. 203 | - =flycheck-info=: number of flycheck notifications, integrates with =flycheck=. 204 | - =minor-modes=: the currently enabled minor modes. The output of this segment 205 | can be tweaked with [[https://github.com/emacsmirror/diminish][Diminish]]. 206 | - =process=: the background process associated with the buffer, if any. 207 | - =erc-track=: IRC channels with new messages, integrates with =erc=. 208 | - =version-control=: version control information. 209 | - =org-pomodoro=: integrates with =org-pomodoro=. 210 | - =org-clock=: the current org clock, integrates with =org=. 211 | - =nyan-cat=: integrates with =nyan-mode=. 212 | - =battery=: integrates with =fancy-battery-mode=. 213 | - =which-function=: integrates with =which-function-mode=. 214 | - =python-pyvenv=: integrates with =pyvenv=. 215 | - =python-pyenv=: integrates with =pyenv=. 216 | - =paradox-menu=: integrates with =paradox=. 217 | - =selection-info=: information about the currently active selection, if any. 218 | - =input-method=: shows the current active input method, if any. 219 | - =buffer-encoding-abbrev=: the line ending convention used in the current 220 | buffer (unix, dos or mac). 221 | - =point-position=: the value of point (disabled by default). 222 | - =line-column=: current line and column. 223 | - =global=: meta-segment used by third-party packages. 224 | - =buffer-position=: shows the current position in the buffer as a percentage. 225 | - =hud=: shows the currently visible part of the buffer. 226 | 227 | In addition, the following segments are defined, but are not used in the default 228 | themes. 229 | 230 | - =line=: current line. 231 | - =column=: current column. 232 | - =projectile-root=: root of current projectile project, integrates with 233 | =projectile=. 234 | - =buffer-encoding=: like =buffer-encoding-abbrev=, but not abbreviated. 235 | 236 | For the custom helm modeline, the following segments are used. 237 | 238 | - =helm-buffer-id=: the name of the current helm session. 239 | - =helm-number=: number of helm candidates. 240 | - =helm-help=: a brief help string. 241 | - =helm-prefix-argument=: shows the prefix argument, if any. 242 | - =helm-follow=: shows whether =helm-follow= is turned on. 243 | 244 | For the custom info modeline, the following segments are used. 245 | 246 | - =info-topic=: the current topic. 247 | - =info-nodes=: breadcrumbs. 248 | 249 | ** The highlight face 250 | The highlight face is the face that (by default) is a sharp orange, used e.g. by 251 | the HUD segment on the far right, and the first segment on the left (note that 252 | it may be invisible if you are using the Spacemacs theme but not some of its 253 | optional dependencies). The actual face used as a highlight face is determined 254 | by a function, which can be configured by setting the value of 255 | =spaceline-highlight-face-func=. Spaceline comes with three choices, but of 256 | course you can write your own: 257 | 258 | - =spaceline-highlight-face-default=: Uses the orange, all the time. This is the 259 | default. 260 | - =spaceline-highlight-face-evil-state=: Chooses a face determined by the 261 | current evil state. The face corresponding to each state is determined by the 262 | association list =spaceline-evil-state-faces=, which contains default values 263 | for the standard evil states. (Spacemacs has a few more.) 264 | - =spaceline-highlight-face-modified=: Chooses a face determined by the status 265 | of the current buffer (modified, unmodified or read-only). 266 | 267 | Note that the highlight face is only used in the active window. 268 | 269 | ** Other faces 270 | In the active window, the mode-line will use these faces: 271 | 272 | - =powerline-active1= 273 | - =powerline-active2= 274 | - =mode-line= 275 | 276 | And in inactive windows: 277 | 278 | - =powerline-inactive1= 279 | - =powerline-inactive2= 280 | - =mode-line-inactive= 281 | 282 | To override this, you can set the variable =spaceline-face-func=. This should be 283 | a function that accepts two arguments and returns a face symbol. The arguments 284 | are: 285 | 286 | - =face=: either of =face1=, =face2=, =line= and =highlight=. 287 | - =active=: a boolean determining whether the window is active or not. 288 | 289 | If this function is not set, Spaceline delegates the highlight face to 290 | =spaceline-highlight-face-func= (see above), and picks the others according to 291 | the above scheme. 292 | 293 | ** Powerline separators 294 | Set =powerline-default-separator= to configure this. The docstring for that 295 | variable enumerates the choices. 296 | 297 | Each separator comes in two directions: left and right. The variables 298 | =spaceline-separator-dir-left= and =spaceline-separator-dir-right= specify which 299 | directions to alternate between on the left and right side, respectively. 300 | 301 | By default these variables are set to =nil=, which means Spaceline will choose 302 | the directions that look best for your chosen separator style. However, you can 303 | set to override this, for example: 304 | 305 | #+BEGIN_SRC emacs-lisp 306 | (setq spaceline-separator-dir-left '(left . left)) 307 | (setq spaceline-separator-dir-right '(right . right)) 308 | #+END_SRC 309 | 310 | Note that you must recompile the modelines after changing the separators, by 311 | calling =M-x spaceline-compile=. 312 | 313 | ** Hooks 314 | The hook =spaceline-pre-hook= is executed before rendering the modeline. Don’t 315 | put any performance-intensive functions here! 316 | 317 | ** Unicode numbers 318 | By default, Spacemacs displays window numbers and workspace numbers in nice 319 | unicode symbols. To do this in Spaceline, set =spaceline-window-numbers-unicode= 320 | or =spaceline-workspace-numbers-unicode= to true, respectively. 321 | 322 | Spacemacs also does this with most minor modes. This is a feature that has not 323 | been ported to Spaceline. To do this, use [[https://github.com/emacsmirror/diminish][Diminish]]. 324 | 325 | ** Minor modes separator 326 | To configure the separator between the minor modes, use 327 | =spaceline-minor-modes-separator=. 328 | 329 | ** Org clock 330 | The displayed value of the =org-clock= segment is determined by the function 331 | =org-clock-get-clock-string= by default. To configure another function, use 332 | =spaceline-org-clock-format-function=. 333 | 334 | * Deep configuration 335 | To understand how to do this, we must first understand how Spaceline constructs 336 | a mode-line. 337 | 338 | ** Segments 339 | A /segment/ is any part of the mode-line that produces some kind of visible 340 | output. Typically, segments have been defined ahead of time using 341 | =spaceline-define-segment=, in which case the segment is referred to by a 342 | symbol, but segments may also be literals (strings or numbers, say) or lists of 343 | other segments. 344 | 345 | These are all valid segments, provided =my-segment= has been defined: 346 | 347 | #+BEGIN_SRC emacs-lisp 348 | my-segment 349 | "alfa" 350 | (my-segment 89) 351 | #+END_SRC 352 | 353 | Segments may also have properties associated with them. Spaceline supports a 354 | variety of properties. They can be applied as follows, for a ‘singleton’ 355 | segment: 356 | 357 | #+BEGIN_SRC emacs-lisp 358 | (my-segment :prop-a value-a :prop-b value-b) 359 | #+END_SRC 360 | 361 | Or for a list of segments: 362 | 363 | #+BEGIN_SRC emacs-lisp 364 | ((my-segment 89) 365 | :prop-a value-a 366 | :prop-b value-b) 367 | #+END_SRC 368 | 369 | ** Defining a segment 370 | Use =spaceline-define-segment= to define a segment and associate it to a symbol. 371 | 372 | #+BEGIN_SRC emacs-lisp 373 | (spaceline-define-segment name 374 | "Docstring" 375 | ;; A single form whose value is the value of the segment. 376 | ;; It may return a string, an image or a list of such. 377 | (when condition 378 | output) 379 | 380 | ;; Additional keyword properties go here 381 | :prop-a value-a 382 | :prop-b value-b) 383 | #+END_SRC 384 | 385 | In addition to storing the segment, this macro produces a variable called 386 | =spaceline-NAME-p= whose value may be set to switch the segment off or on 387 | manually. Three interactive functions are also defined: 388 | 389 | - =spaceline-toggle-NAME= 390 | - =spaceline-toggle-NAME-on= 391 | - =spaceline-toggle-NAME-off= 392 | 393 | These are convenient to bind to keys, and they do what it says on the tin. 394 | 395 | Note that if you redefine a segment, you more than likely have to recompile the 396 | modelines with =M-x spaceline-compile= for the changes to take effect. 397 | 398 | ** Properties 399 | The valid properties are 400 | 401 | - =:priority=: arbitrary number to prioritize which segments are hidden first 402 | when the window shrinks. The higher the number, the higher the priority. 403 | - =:when=: A form that, if it evaluates to =nil=, will prevent the segment from 404 | showing. Note that in =spaceline-define-segment= you might just as well use an 405 | ordinary =when= form. Therefore this only makes sense to use in a segment 406 | spec. 407 | - =:separator=: A separator inserted between each element of the value of the 408 | given segment. This makes most sense for lists of segments, or segments whose 409 | values are typically lists (such as =minor-modes=). 410 | - =:fallback=: A segment which will be displayed in place of the current segment 411 | if it should produce no output (either due to a nil =:when= condition or 412 | because the return value of the segment itself is =nil= or the empty string). 413 | - =:face=: The face in which to render the segment. It may be better to use this 414 | than (or in addition) to propertizing the output directly, since Spaceline 415 | needs to know the faces to propertize the separators correctly. This may be 416 | either =default-face=, =other-face= or =highlight-face=, or a form evaluating 417 | to a face. Thus any face symbol which is not either of the above three must be 418 | quoted. 419 | - =:tight=: Set to =t= to tell Spaceline that the segment should not have any 420 | padding on the right or left. Use =:tight-left= and =:tight-right= for even 421 | finer control. 422 | - =:skip-alternate=: Set to =t= to skip the regular alternating faces for this 423 | segment. 424 | 425 | All of these are valid both in =spaceline-define-segment= as well as directly in 426 | the segment spec, with the excption of =:when=. 427 | 428 | Additionally, =spaceline-define-segment= allows two additional properties. 429 | 430 | - =:enabled=: Sets the initial value of the toggle variable. 431 | - =:global-override=: Many third-party packages provide mode-line information by 432 | inserting a segment in the list =global-mode-string=. Sometimes you might like 433 | to write your own segment for this, in which case you have to prevent the 434 | package from using =global-mode-string=, or you will end up with duplicate 435 | information and a crowded mode-line. To do this, set =:global-override= to the 436 | symbol (or list of symbols) which you want to exclude from 437 | =global-mode-string=. This setting will be honored by the =global= segment, 438 | which is defined by Spaceline core in =spaceline.el=. 439 | 440 | The properties which take effect for any given segment are, in order of 441 | priority: 442 | 443 | - the properties specified in the segment specification 444 | - the properties given in the call to =spaceline-define-segment= 445 | - the properties of the parent segment 446 | 447 | The exceptions are =:when=, which must be true on *all* levels for a segment to 448 | be displayed, and =:fallback= which does *not* pass through from the parent 449 | segment. 450 | 451 | ** Bindings 452 | When evaluating a segment, its =:when= condition or its =:face= property, the 453 | following bindings are available for convenience. 454 | 455 | - =active=: Whether the current window is active or not. Many segments use 456 | =:when active= to only show in the current window. 457 | - =default-face=: The face with which the current segment /should/ be rendered. 458 | If you don’t define a =:face=, this is what you get. For best results, stick 459 | to the default face as often as you can. 460 | - =other-face=: The alternating default face. Spaceline switches =default-face= 461 | and =other-face= for each top-level segment. 462 | - =highlight-face=: The face used to highlight ‘important’ parts, whatever that 463 | may be. This may be customized. 464 | - =line-face=: The face with which the empty part in the middle of the mode-line 465 | will be rendered. 466 | 467 | Note that the segment code runs in an environment with many local variables, 468 | therefore it’s a good idea to write segments as pure functions that do not 469 | change state. 470 | 471 | ** Compiling a mode-line 472 | Finally, call the function =spaceline-compile=. It accepts three arguments: a 473 | modeline name, and two lists of segments, for the left and right sides. 474 | 475 | This produces a function =spaceline-ml-NAME= that evaluates the mode-line. To 476 | use it, set =mode-line-format= to 477 | 478 | #+BEGIN_SRC emacs-lisp 479 | ("%e" (:eval (spaceline-ml-NAME))) 480 | #+END_SRC 481 | 482 | If you do not specify a name, the modeline will be installed as =main=. 483 | 484 | If you do not specify segment lists, it will either recompile the given modeline 485 | with the segments specified last time, or recompile /all/ modelines if the name 486 | is not specified. 487 | 488 | When called interactively, the latter behaviour takes effect, that is, all 489 | modelines are recompiled. 490 | 491 | The variable =spaceline-byte-compile= decides whether the resulting function 492 | will be byte-compiled. This is recommended for regular usage, as it involves 493 | potentially significant performance benefits. 494 | 495 | * Tweaking or defining your own mode-line 496 | To tweak the properties such as =:when= or =:priority= of specific segments, 497 | or having your own selection of segments and order of appearance, you have to 498 | define your own mode-line and =spaceline-compile= it. 499 | 500 | For instance, to use Spacemac's mode-line definition as a starting point to 501 | your own, add this to your =.emacs= or =.spacemacs= and tweak it: 502 | 503 | #+BEGIN_SRC emacs-lisp 504 | (spaceline-compile 505 | ; left side 506 | '(((persp-name 507 | workspace-number 508 | window-number) 509 | :fallback evil-state 510 | :face highlight-face 511 | :priority 100) 512 | (anzu :priority 95) 513 | auto-compile 514 | ((buffer-modified buffer-size buffer-id remote-host) 515 | :priority 98) 516 | (major-mode :priority 79) 517 | (process :when active) 518 | ((flycheck-error flycheck-warning flycheck-info) 519 | :when active 520 | :priority 89) 521 | (minor-modes :when active 522 | :priority 9) 523 | (mu4e-alert-segment :when active) 524 | (erc-track :when active) 525 | (version-control :when active 526 | :priority 78) 527 | (org-pomodoro :when active) 528 | (org-clock :when active) 529 | nyan-cat) 530 | ; right side 531 | '(which-function 532 | (python-pyvenv :fallback python-pyenv) 533 | (purpose :priority 94) 534 | (battery :when active) 535 | (selection-info :priority 95) 536 | input-method 537 | ((buffer-encoding-abbrev 538 | point-position 539 | line-column) 540 | :separator " | " 541 | :priority 96) 542 | (global :when active) 543 | (buffer-position :priority 99) 544 | (hud :priority 99))) 545 | #+END_SRC 546 | -------------------------------------------------------------------------------- /screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBB/spaceline/086420d16e526c79b67fc1edec4c2ae1e699f372/screen.png -------------------------------------------------------------------------------- /spaceline-config.el: -------------------------------------------------------------------------------- 1 | ;;; spaceline-config.el --- Spaceline themes 2 | 3 | ;; This file is not part of GNU Emacs. 4 | 5 | ;; This program is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation, either version 3 of the License, or 8 | ;; (at your option) any later version. 9 | 10 | ;; This program is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see . 17 | 18 | ;;; Commentary: 19 | 20 | ;; This file contains ready-to-use modeline themes for powerline. 21 | 22 | ;;; Code: 23 | 24 | (require 'spaceline-segments) 25 | 26 | (defun spaceline--theme (left second-left &rest additional-segments) 27 | "Convenience function for the spacemacs and emacs themes." 28 | (spaceline-compile 29 | `(,left 30 | (anzu :priority 95) 31 | auto-compile 32 | ,second-left 33 | (major-mode :priority 79) 34 | (process :when active) 35 | ((flycheck-error flycheck-warning flycheck-info) 36 | :when active 37 | :priority 89) 38 | (minor-modes :when active 39 | :priority 9) 40 | (treesit-inspect :when active) 41 | (mu4e-alert-segment :when active) 42 | (erc-track :when active) 43 | (version-control :when active 44 | :priority 78) 45 | (org-pomodoro :when active) 46 | (org-clock :when active) 47 | nyan-cat) 48 | `(which-function 49 | (python-pyvenv :fallback python-pyenv) 50 | (purpose :priority 94) 51 | (battery :when active) 52 | (selection-info :priority 95) 53 | input-method 54 | ((buffer-encoding-abbrev 55 | point-position 56 | line-column) 57 | :separator " | " 58 | :priority 96) 59 | (so-long :when active) 60 | (global :when active) 61 | ,@additional-segments 62 | (buffer-position :priority 99) 63 | (hud :priority 99))) 64 | 65 | (setq-default mode-line-format '("%e" (:eval (spaceline-ml-main))))) 66 | 67 | ;;;###autoload 68 | (defun spaceline-spacemacs-theme (&rest additional-segments) 69 | "Install the modeline used by Spacemacs. 70 | 71 | ADDITIONAL-SEGMENTS are inserted on the right, between `global' and 72 | `buffer-position'." 73 | (apply 'spaceline--theme 74 | '((persp-name 75 | workspace-number 76 | window-number) 77 | :fallback evil-state 78 | :face highlight-face 79 | :priority 100) 80 | '((buffer-modified buffer-size buffer-id remote-host) 81 | :priority 98) 82 | additional-segments)) 83 | 84 | ;;;###autoload 85 | (defun spaceline-emacs-theme (&rest additional-segments) 86 | "Install a modeline close to the one used by Spacemacs, but which 87 | looks better without third-party dependencies. 88 | 89 | ADDITIONAL-SEGMENTS are inserted on the right, between `global' and 90 | `buffer-position'." 91 | (apply 'spaceline--theme 92 | '(((((persp-name :fallback workspace-number) 93 | window-number) :separator "|") 94 | buffer-modified 95 | buffer-size) 96 | :face highlight-face 97 | :priority 100) 98 | '((buffer-id remote-host) 99 | :priority 98) 100 | additional-segments)) 101 | 102 | ;;; Helm custom mode 103 | ;; ================ 104 | 105 | (defvar helm-ag-show-status-function) 106 | 107 | (defun spaceline--helm-ag-update () 108 | (setq mode-line-format '("%e" (:eval (spaceline-ml-helm-done))))) 109 | 110 | ;;;###autoload 111 | (define-minor-mode spaceline-helm-mode 112 | "Customize the mode-line in helm." 113 | :group 'spaceline 114 | :init-value nil 115 | :global t 116 | (if spaceline-helm-mode 117 | (progn 118 | (spaceline-compile 'helm 119 | '((helm-buffer-id :face highlight-face) 120 | helm-number 121 | helm-follow 122 | helm-prefix-argument) 123 | '(helm-help)) 124 | (spaceline-compile 'helm-done 125 | '(((helm-buffer-id helm-done) :face highlight-face) 126 | helm-number 127 | helm-follow 128 | helm-prefix-argument) 129 | '(helm-help)) 130 | (defadvice helm-display-mode-line (after spaceline-helm) 131 | "Set up a custom helm modeline." 132 | (setq spaceline--helm-current-source source 133 | mode-line-format '("%e" (:eval (spaceline-ml-helm)))) 134 | (when force (force-mode-line-update))) 135 | (setq helm-ag-show-status-function 'spaceline--helm-ag-update) 136 | (ad-activate 'helm-display-mode-line)) 137 | (setq helm-ag-show-status-function 'helm-ag-show-status-default-mode-line) 138 | (ad-deactivate 'helm-display-mode-line))) 139 | 140 | ;;; Info custom mode 141 | ;; ================ 142 | 143 | ;;;###autoload 144 | (define-minor-mode spaceline-info-mode 145 | "Customize the mode-line in info. 146 | This minor mode requires info+." 147 | :init-value nil 148 | :group 'spaceline 149 | :global t 150 | (if spaceline-info-mode 151 | (progn 152 | (spaceline-compile 'info '(info-topic (info-nodes :separator " > ")) nil) 153 | (defadvice Info-set-mode-line (after spaceline-info) 154 | "Set up a custom info modeline." 155 | (if (featurep 'info+) 156 | (let* ((nodes (s-split " > " mode-line-format)) 157 | (topic (prog2 158 | (string-match "(\\(.+\\))\\(.+\\)" (car nodes)) 159 | (propertize (concat "INFO " 160 | (match-string 1 (car nodes))) 161 | 'face 'bold) 162 | (setcar nodes (match-string 2 (car nodes)))))) 163 | (setq spaceline--info-nodes nodes) 164 | (setq spaceline--info-topic topic) 165 | (setq-local mode-line-format '("%e" (:eval (spaceline-ml-info))))) 166 | (message "info+ is not available: spaceline-info-mode disabled") 167 | (spaceline-info-mode -1))) 168 | (ad-activate 'Info-set-mode-line)) 169 | (ad-deactivate 'Info-set-mode-line))) 170 | 171 | (provide 'spaceline-config) 172 | 173 | ;;; spaceline-config.el ends here 174 | -------------------------------------------------------------------------------- /spaceline-segments.el: -------------------------------------------------------------------------------- 1 | ;;; spaceline-segments.el --- Segments for spaceline 2 | 3 | ;; This file is not part of GNU Emacs. 4 | 5 | ;; This program is free software; you can redistribute it and/or modify 6 | ;; it under the terms of the GNU General Public License as published by 7 | ;; the Free Software Foundation, either version 3 of the License, or 8 | ;; (at your option) any later version. 9 | 10 | ;; This program is distributed in the hope that it will be useful, 11 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ;; GNU General Public License for more details. 14 | 15 | ;; You should have received a copy of the GNU General Public License 16 | ;; along with this program. If not, see . 17 | 18 | ;;; Commentary: 19 | 20 | ;; This file contains a variety of segments that may be of general interest in 21 | ;; many people's modelines. It contains both "stock" segments which are usable 22 | ;; without any additional packages, as well as a number of segments which depend 23 | ;; on optional third-party packages. 24 | 25 | ;; Note: The `global' segment is defined in spaceline.el, not here. It's the 26 | ;; only exception. 27 | 28 | ;;; Code: 29 | 30 | (require 'spaceline) 31 | (require 's) 32 | 33 | (defvar evil-state) 34 | (defvar evil-visual-selection) 35 | 36 | ;;; Stock segments - no optional dependencies 37 | ;; ========================================= 38 | 39 | (defvar spaceline-minor-modes-separator "|") 40 | (spaceline-define-segment minor-modes 41 | "A list of minor modes. Configure the separator with 42 | `spaceline-minor-modes-separator'." 43 | (-filter 44 | (lambda (k) (and k (not (string= k "")))) 45 | (mapcar (lambda (mm) 46 | (let* ((displayp (and (boundp (car mm)) 47 | (symbol-value (car mm)))) 48 | (lighter (when displayp 49 | (s-trim (format-mode-line (cadr mm))))) 50 | (displayp (and lighter (not (string= "" lighter))))) 51 | (when displayp 52 | (propertize 53 | lighter 54 | 'mouse-face 'mode-line-highlight 55 | 'help-echo (concat (symbol-name (car mm)) 56 | "\nmouse-1: Display minor mode menu" 57 | "\nmouse-2: Show help for minor mode" 58 | "\nmouse-3: Toggle minor mode") 59 | 'local-map (let ((map (make-sparse-keymap))) 60 | (define-key map 61 | [mode-line mouse-1] 62 | (powerline-mouse 'minor 'menu lighter)) 63 | (define-key map 64 | [mode-line mouse-2] 65 | (powerline-mouse 'minor 'help lighter)) 66 | (define-key map 67 | [mode-line mouse-3] 68 | (powerline-mouse 'minor 'menu lighter)) 69 | (define-key map 70 | [header-line down-mouse-3] 71 | (powerline-mouse 'minor 'menu lighter)) 72 | map))))) 73 | minor-mode-alist)) 74 | :separator spaceline-minor-modes-separator) 75 | 76 | (spaceline-define-segment buffer-modified 77 | "Buffer modified marker." 78 | "%*") 79 | 80 | (spaceline-define-segment buffer-size 81 | "Size of buffer." 82 | (if (bound-and-true-p total-lines-mode) 83 | (format "%d" total-lines) 84 | (powerline-buffer-size))) 85 | 86 | (defcustom spaceline-buffer-id-max-length 45 87 | "The maximum displayed length of the buffer-id segment." 88 | :type 'integer 89 | :group 'spaceline) 90 | 91 | (spaceline-define-segment buffer-id 92 | "Name of buffer." 93 | (spaceline--string-trim-from-center 94 | (s-trim (powerline-buffer-id (if active 'mode-line-buffer-id 'mode-line-buffer-id-inactive))) 95 | spaceline-buffer-id-max-length)) 96 | 97 | (spaceline-define-segment remote-host 98 | "Hostname for remote buffers." 99 | (when (and default-directory 100 | (file-remote-p default-directory 'host)) 101 | (concat "@" (file-remote-p default-directory 'host)))) 102 | 103 | (spaceline-define-segment major-mode 104 | "The name of the major mode." 105 | (powerline-major-mode)) 106 | 107 | (spaceline-define-segment process 108 | "The process associated with this buffer, if any." 109 | (when (spaceline--mode-line-nonempty mode-line-process) 110 | (s-trim (powerline-raw mode-line-process)))) 111 | 112 | (spaceline-define-segment version-control-simple 113 | "Simplified version control information." 114 | (when vc-mode 115 | (powerline-raw 116 | (when (buffer-file-name) 117 | (pcase (vc-state (buffer-file-name)) 118 | (`up-to-date " ") 119 | (`ignored " ") 120 | (`edited "~") 121 | (`added "+") 122 | (`removed "-") 123 | (`needs-merge "!") 124 | (`needs-update "!") 125 | (_ "?")))))) 126 | 127 | (spaceline-define-segment version-control 128 | "Version control information." 129 | (when vc-mode 130 | (powerline-raw 131 | (s-trim (concat vc-mode 132 | (when (buffer-file-name) 133 | (pcase (vc-state (buffer-file-name)) 134 | (`up-to-date " ") 135 | (`edited " Mod") 136 | (`added " Add") 137 | (`unregistered " ??") 138 | (`removed " Del") 139 | (`needs-merge " Con") 140 | (`needs-update " Upd") 141 | (`ignored " Ign") 142 | (_ " Unk")))))))) 143 | 144 | (spaceline-define-segment buffer-encoding 145 | "The full `buffer-file-coding-system'." 146 | (format "%s" buffer-file-coding-system)) 147 | 148 | (spaceline-define-segment buffer-encoding-abbrev 149 | "The line ending convention used in the buffer." 150 | (let ((buf-coding (format "%s" buffer-file-coding-system))) 151 | (if (string-match "\\(dos\\|unix\\|mac\\)" buf-coding) 152 | (match-string 1 buf-coding) 153 | buf-coding))) 154 | 155 | (spaceline-define-segment point-position 156 | "The value of `(point)'." 157 | (format "%d" (point)) 158 | :enabled nil) 159 | 160 | (spaceline-define-segment line 161 | "The current line number." 162 | "%l") 163 | 164 | (spaceline-define-segment column 165 | "The current column number." 166 | (if (and 167 | (boundp 'column-number-indicator-zero-based) 168 | (not column-number-indicator-zero-based)) 169 | "%2C" 170 | "%2c")) 171 | 172 | (defun spaceline--docview-page-number () 173 | "The current `doc-view-mode' page number to display in the mode-line. 174 | Return a formated string containing the current and last page number for the 175 | currently displayed pdf file in `doc-view-mode'." 176 | (format "(%d/%d)" 177 | (image-mode-window-get 'page nil) 178 | (doc-view-last-page-number))) 179 | 180 | (declare-function pdf-view-current-page 'pdf-view) 181 | (declare-function pdf-cache-number-of-pages 'pdf-view) 182 | 183 | (defun spaceline--pdfview-page-number () 184 | "The current `pdf-view-mode' page number to display in the mode-line. 185 | Return a formated string containing the current and last page number for the 186 | currently displayed pdf file in `pdf-view-mode'." 187 | (format "(%d/%d)" 188 | ;; `pdf-view-current-page' is a macro in an optional dependency 189 | ;; any better solutions? 190 | (eval `(pdf-view-current-page)) 191 | (pdf-cache-number-of-pages))) 192 | 193 | (spaceline-define-segment line-column 194 | "The current line and column numbers, or `(current page/number of pages)` 195 | in pdf-view mode (enabled by the `pdf-tools' package)." 196 | (cond ((eq major-mode 'doc-view-mode) 197 | (spaceline--docview-page-number)) 198 | ((eq major-mode 'pdf-view-mode) 199 | (spaceline--pdfview-page-number)) 200 | (t (if (and 201 | (boundp 'column-number-indicator-zero-based) 202 | (not column-number-indicator-zero-based)) 203 | "%l:%2C" 204 | "%l:%2c")))) 205 | 206 | (spaceline-define-segment buffer-position 207 | "The current approximate buffer position, in percent." 208 | "%p") 209 | 210 | (defun spaceline--column-number-at-pos (pos) 211 | "Column number at POS. Analog to `line-number-at-pos'." 212 | (save-excursion (goto-char pos) (current-column))) 213 | 214 | (spaceline-define-segment selection-info 215 | "Information about the size of the current selection, when applicable. 216 | Supports both Emacs and Evil cursor conventions." 217 | (when (or mark-active 218 | (and (bound-and-true-p evil-local-mode) 219 | (eq 'visual evil-state))) 220 | (let* ((lines (count-lines (region-beginning) (min (1+ (region-end)) (point-max)))) 221 | (chars (- (1+ (region-end)) (region-beginning))) 222 | (cols (1+ (abs (- (spaceline--column-number-at-pos (region-end)) 223 | (spaceline--column-number-at-pos (region-beginning)))))) 224 | (evil (and (bound-and-true-p evil-state) (eq 'visual evil-state))) 225 | (rect (or (bound-and-true-p rectangle-mark-mode) 226 | (and evil (eq 'block evil-visual-selection)))) 227 | (multi-line (or (> lines 1) (and evil (eq 'line evil-visual-selection))))) 228 | (cond 229 | (rect (format "%d×%d block" lines (if evil cols (1- cols)))) 230 | (multi-line (format "%d lines" lines)) 231 | (t (format "%d chars" (if evil chars (1- chars)))))))) 232 | 233 | (defcustom spaceline-show-default-input-method nil 234 | "Whether to show the default input method in `input-method'. 235 | 236 | When non-nil, show the default input method in the `input-method' 237 | segment. Otherwise only show the active input method, if any." 238 | :type 'boolean 239 | :group 'spaceline 240 | :risky t) 241 | 242 | (spaceline-define-segment input-method 243 | "The current input method, or the default input method." 244 | (when (or current-input-method 245 | (and (bound-and-true-p evil-mode) 246 | (bound-and-true-p evil-input-method)) 247 | (and spaceline-show-default-input-method 248 | default-input-method)) 249 | (cond 250 | (current-input-method 251 | (propertize current-input-method-title 'face 'bold)) 252 | ;; `evil-input-method' is where evil remembers the input method while in 253 | ;; normal state. The input method is not active in normal state, but Evil 254 | ;; will enable this input method again when switching to insert/emacs state. 255 | ((and (bound-and-true-p evil-mode) (bound-and-true-p evil-input-method)) 256 | (nth 3 (assoc default-input-method input-method-alist))) 257 | ((and spaceline-show-default-input-method default-input-method) 258 | (propertize (nth 3 (assoc default-input-method input-method-alist)) 259 | 'face 'italic))))) 260 | 261 | (spaceline-define-segment hud 262 | "A HUD that shows which part of the buffer is currently visible." 263 | (powerline-hud highlight-face default-face) 264 | :tight t) 265 | 266 | ;;; Helm segments 267 | ;; ============= 268 | 269 | (declare-function helm-candidate-number-at-point 'helm) 270 | (declare-function helm-get-candidate-number 'helm) 271 | 272 | (defvar spaceline--helm-buffer-ids 273 | '(("*helm*" . "HELM") 274 | ("*helm M-x*" . "HELM M-x") 275 | ("*swiper*" . "SWIPER") 276 | ("*Projectile Perspectives*" . "HELM Projectile Perspectives") 277 | ("*Projectile Layouts*" . "HELM Projectile Layouts") 278 | ("*helm-ag*" . (lambda () 279 | (format "HELM Ag: Using %s" 280 | (car (split-string helm-ag-base-command)))))) 281 | "Alist of custom helm buffer names to use. 282 | The cdr can also be a function that returns a name to use.") 283 | (spaceline-define-segment helm-buffer-id 284 | "Helm session identifier." 285 | (when (bound-and-true-p helm-alive-p) 286 | (propertize 287 | (let ((custom (cdr (assoc (buffer-name) spaceline--helm-buffer-ids))) 288 | (case-fold-search t) 289 | (name (replace-regexp-in-string "-" " " (buffer-name)))) 290 | (cond ((stringp custom) custom) 291 | ((functionp custom) (funcall custom)) 292 | (t 293 | (string-match "\\*helm:? \\(mode \\)?\\([^\\*]+\\)\\*" name) 294 | (concat "HELM " (capitalize (match-string 2 name)))))) 295 | 'face 'bold))) 296 | 297 | (spaceline-define-segment helm-done 298 | "Done." 299 | (propertize "(DONE)" 'face 'bold)) 300 | 301 | (spaceline-define-segment helm-number 302 | "Number of helm candidates." 303 | (when (bound-and-true-p helm-alive-p) 304 | (format "%d/%s (%s total)" 305 | (helm-candidate-number-at-point) 306 | (helm-get-candidate-number t) 307 | (helm-get-candidate-number)))) 308 | 309 | (spaceline-define-segment helm-help 310 | "Helm keybindings help." 311 | (when (bound-and-true-p helm-alive-p) 312 | (-interleave 313 | (mapcar (lambda (s) 314 | (propertize (substitute-command-keys s) 'face 'bold)) 315 | '("\\\\[helm-help]" 316 | "\\\\[helm-select-action]" 317 | "\\\\[helm-maybe-exit-minibuffer]/F1/F2...")) 318 | '("(help)" "(actions)" "(action)")))) 319 | 320 | (spaceline-define-segment helm-prefix-argument 321 | "Helm prefix argument." 322 | (when (and (bound-and-true-p helm-alive-p) 323 | helm--mode-line-display-prefarg) 324 | (let ((arg (prefix-numeric-value (or prefix-arg current-prefix-arg)))) 325 | (unless (= arg 1) 326 | (propertize (format "C-u %s" arg) 'face 'helm-prefarg))))) 327 | 328 | (defvar spaceline--helm-current-source nil 329 | "The currently active helm source.") 330 | (spaceline-define-segment helm-follow 331 | "Helm follow indicator." 332 | (when (and (bound-and-true-p helm-alive-p) 333 | spaceline--helm-current-source 334 | (eq 1 (cdr (assq 'follow spaceline--helm-current-source)))) 335 | "HF")) 336 | 337 | ;;; Info segments 338 | ;; ============= 339 | 340 | (defvar spaceline--info-topic nil 341 | "Topic for the info modeline.") 342 | (spaceline-define-segment info-topic 343 | "Topic for the info modeline." 344 | spaceline--info-topic) 345 | (defvar spaceline--info-nodes nil 346 | "Breadcrumbs for the info modeline.") 347 | (spaceline-define-segment info-nodes 348 | "Breadcrumbs for the info modeline." 349 | spaceline--info-nodes) 350 | 351 | ;;; Segments requiring optional dependencies 352 | ;; ======================================== 353 | 354 | (defvar conda-env-current-name) 355 | (defvar erc-modified-channels-object) 356 | (defvar erc-track-position-in-mode-line) 357 | (defvar fancy-battery-last-status) 358 | (defvar fancy-battery-show-percentage) 359 | (defvar mu4e-alert-mode-line) 360 | (defvar org-pomodoro-mode-line) 361 | (defvar pyvenv-virtual-env) 362 | (defvar pyvenv-virtual-env-name) 363 | (defvar which-func-current) 364 | (defvar which-func-keymap) 365 | 366 | (declare-function projectile-project-name 'projectile) 367 | (declare-function anzu--update-mode-line 'anzu) 368 | (declare-function evil-state-property 'evil-common) 369 | (declare-function eyebrowse--get 'eyebrowse) 370 | (declare-function mode-line-auto-compile-control 'auto-compile) 371 | (declare-function nyan-create 'nyan-mode) 372 | (declare-function safe-persp-name 'persp-mode) 373 | (declare-function get-frame-persp 'persp-mode) 374 | (declare-function winum-get-number 'winum) 375 | (declare-function window-numbering-get-number 'window-numbering) 376 | (declare-function purpose--modeline-string 'window-purpose) 377 | (declare-function purpose-window-purpose-dedicated-p 'window-purpose) 378 | (declare-function pyenv-mode-version 'pyenv-mode) 379 | (declare-function pyenv-mode-full-path 'pyenv-mode) 380 | (declare-function doc-view-last-page-number 'doc-view) 381 | (declare-function image-mode-window-get 'image-mode) 382 | 383 | (spaceline-define-segment projectile-root 384 | "Show the current projectile root." 385 | (when (fboundp 'projectile-project-name) 386 | (let ((project-name (projectile-project-name))) 387 | (unless (or (string= project-name "-") 388 | (string= project-name (buffer-name))) 389 | project-name)))) 390 | 391 | (spaceline-define-segment anzu 392 | "Show the current match number and the total number of matches. 393 | Requires anzu to be enabled." 394 | (when (and active (bound-and-true-p anzu--state)) 395 | (anzu--update-mode-line))) 396 | 397 | (spaceline-define-segment auto-compile 398 | "Show the count of byte-compiler warnings from the auto-compile 399 | package." 400 | (when (and active (boundp 'auto-compile-warnings) (> auto-compile-warnings 0)) 401 | (mode-line-auto-compile-control))) 402 | 403 | (spaceline-define-segment erc-track 404 | "Show the ERC buffers with new messages. Requires 405 | `erc-track-mode' to be enabled and 406 | `erc-track-position-in-mode-line' to be set to true." 407 | (when (and (bound-and-true-p erc-track-mode) 408 | erc-track-position-in-mode-line 409 | erc-modified-channels-object) 410 | (s-trim erc-modified-channels-object)) 411 | :global-override erc-modified-channels-object) 412 | 413 | (defun spaceline--fancy-battery-percentage () 414 | "Return the load percentage or an empty string." 415 | (let ((p (cdr (assq ?p fancy-battery-last-status)))) 416 | (if (and fancy-battery-show-percentage 417 | p (not (string= "N/A" p))) (concat " " p "%%") ""))) 418 | 419 | (defun spaceline--fancy-battery-time () 420 | "Return the remaining time complete load or discharge." 421 | (let ((time (cdr (assq ?t fancy-battery-last-status)))) 422 | (cond 423 | ((string= "0:00" time) "") 424 | ((string= "N/A" time) "") 425 | ((string= time "") "") 426 | (t (concat " (" time ")"))))) 427 | 428 | (defun spaceline--fancy-battery-mode-line () 429 | "Assemble a mode line string for Fancy Battery Mode." 430 | (when fancy-battery-last-status 431 | (let* ((type (cdr (assq ?L fancy-battery-last-status))) 432 | (percentage (spaceline--fancy-battery-percentage)) 433 | (time (spaceline--fancy-battery-time))) 434 | (cond 435 | ((string= "on-line" type) " No Battery") 436 | ((string= type "") " No Battery") 437 | (t (concat (if (string= "AC" type) " AC" "") percentage time)))))) 438 | 439 | (defun spaceline--fancy-battery-face () 440 | "Return a face appropriate for powerline." 441 | (let ((type (cdr (assq ?L fancy-battery-last-status)))) 442 | (if (and type (string= "AC" type)) 443 | 'fancy-battery-charging 444 | (pcase (cdr (assq ?b fancy-battery-last-status)) 445 | ("!" 'fancy-battery-critical) 446 | ("+" 'fancy-battery-charging) 447 | ("-" 'fancy-battery-discharging) 448 | (_ 'fancy-battery-discharging))))) 449 | 450 | (spaceline-define-segment battery 451 | "Show battery information. Requires `fancy-battery-mode' to be enabled. 452 | 453 | This segment overrides the modeline functionality of 454 | `fancy-battery-mode'." 455 | (when (bound-and-true-p fancy-battery-mode) 456 | (let ((text (spaceline--fancy-battery-mode-line))) 457 | (and text (powerline-raw (s-trim text) 458 | (spaceline--fancy-battery-face))))) 459 | :global-override fancy-battery-mode-line) 460 | 461 | (defvar spaceline-org-clock-format-function 462 | 'org-clock-get-clock-string 463 | "The function called by the `org-clock' segment to determine what to show.") 464 | 465 | (spaceline-define-segment org-clock 466 | "Show information about the current org clock task. Configure 467 | `spaceline-org-clock-format-function' to configure. Requires a currently running 468 | org clock. 469 | 470 | This segment overrides the modeline functionality of `org-mode-line-string'." 471 | (when (and (fboundp 'org-clocking-p) 472 | (org-clocking-p)) 473 | (substring-no-properties (funcall spaceline-org-clock-format-function))) 474 | :global-override org-mode-line-string) 475 | 476 | (spaceline-define-segment org-pomodoro 477 | "Shows the current pomodoro. Requires `org-pomodoro' to be active. 478 | 479 | This segment overrides the modeline functionality of `org-pomodoro' itself." 480 | (when (and (fboundp 'org-pomodoro-active-p) 481 | (org-pomodoro-active-p)) 482 | (nth 1 org-pomodoro-mode-line)) 483 | :global-override org-pomodoro-mode-line) 484 | 485 | (spaceline-define-segment nyan-cat 486 | "Shows the infamous nyan cat. Requires `nyan-mode' to be enabled." 487 | (when (bound-and-true-p nyan-mode) 488 | (powerline-raw (nyan-create) default-face))) 489 | 490 | (defun spaceline--unicode-number (str) 491 | "Return a nice unicode representation of a single-digit number STR." 492 | (cond 493 | ((string= "1" str) "➊") 494 | ((string= "2" str) "➋") 495 | ((string= "3" str) "➌") 496 | ((string= "4" str) "➍") 497 | ((string= "5" str) "➎") 498 | ((string= "6" str) "➏") 499 | ((string= "7" str) "➐") 500 | ((string= "8" str) "➑") 501 | ((string= "9" str) "➒") 502 | ((string= "10" str) "➓") 503 | (t str))) 504 | 505 | (defvar spaceline-window-numbers-unicode nil 506 | "Set to true to enable unicode display in the `window-number' segment.") 507 | 508 | (spaceline-define-segment window-number 509 | "The current window number. 510 | Requires either `winum-mode' or `window-numbering-mode' to be enabled." 511 | (let* ((num (cond 512 | ((bound-and-true-p winum-mode) 513 | (winum-get-number)) 514 | ((bound-and-true-p window-numbering-mode) 515 | (window-numbering-get-number)) 516 | (t nil))) 517 | (str (when num (int-to-string num)))) 518 | (when num 519 | (if spaceline-window-numbers-unicode 520 | (spaceline--unicode-number str) 521 | (propertize str 'face 'bold))))) 522 | 523 | (defvar spaceline-workspace-numbers-unicode nil 524 | "Set to true to enable unicode display in the `workspace-number' segment.") 525 | 526 | (spaceline-define-segment workspace-number 527 | "The current workspace name or number. Requires `eyebrowse-mode' to be 528 | enabled." 529 | (when (and (bound-and-true-p eyebrowse-mode) 530 | (< 1 (length (eyebrowse--get 'window-configs)))) 531 | (let* ((num (eyebrowse--get 'current-slot)) 532 | (tag (when num (nth 2 (assoc num (eyebrowse--get 'window-configs))))) 533 | (str (if (and tag (< 0 (length tag))) 534 | tag 535 | (when num (int-to-string num))))) 536 | (or (when spaceline-workspace-numbers-unicode 537 | (spaceline--unicode-number str)) 538 | (propertize str 'face 'bold))))) 539 | 540 | (defvar spaceline-display-default-perspective nil 541 | "If non-nil, the default perspective name is displayed in the mode-line.") 542 | 543 | (spaceline-define-segment persp-name 544 | "The current perspective name." 545 | (when (and active 546 | (bound-and-true-p persp-mode) 547 | ;; There are multiple implementations of 548 | ;; persp-mode with different APIs 549 | (fboundp 'safe-persp-name) 550 | (fboundp 'get-frame-persp) 551 | ;; Display the nil persp only if specified 552 | (or (not (string= persp-nil-name (safe-persp-name (get-frame-persp)))) 553 | spaceline-display-default-perspective)) 554 | (let ((name (safe-persp-name (get-frame-persp)))) 555 | (propertize 556 | (if (file-directory-p name) 557 | (file-name-nondirectory (directory-file-name name)) 558 | name) 559 | 'face 'bold)))) 560 | 561 | (defface spaceline-flycheck-error 562 | '((t (:foreground "#FC5C94" :distant-foreground "#A20C41"))) 563 | "Face for flycheck error feedback in the modeline." 564 | :group 'spaceline) 565 | (defface spaceline-flycheck-warning 566 | '((t (:foreground "#F3EA98" :distant-foreground "#968B26"))) 567 | "Face for flycheck warning feedback in the modeline." 568 | :group 'spaceline) 569 | (defface spaceline-flycheck-info 570 | '((t (:foreground "#8DE6F7" :distant-foreground "#21889B"))) 571 | "Face for flycheck info feedback in the modeline." 572 | :group 'spaceline) 573 | 574 | (defvar spaceline-flycheck-bullet "•%s" 575 | "The bullet used for the flycheck segment. 576 | This should be a format string with a single `%s'-expression corresponding to 577 | the number of errors.") 578 | 579 | (defmacro spaceline--flycheck-lighter (state) 580 | "Return flycheck information for the given error type STATE." 581 | `(let* ((counts (flycheck-count-errors flycheck-current-errors)) 582 | (errorp (flycheck-has-current-errors-p ',state)) 583 | (err (or (cdr (assq ',state counts)) "?")) 584 | (running (eq 'running flycheck-last-status-change))) 585 | (if (or errorp running) (format spaceline-flycheck-bullet err)))) 586 | 587 | (dolist (state '(error warning info)) 588 | (let ((segment-name (intern (format "flycheck-%S" state))) 589 | (face (intern (format "spaceline-flycheck-%S" state)))) 590 | (eval 591 | `(spaceline-define-segment ,segment-name 592 | ,(format "Information about flycheck %Ss. Requires `flycheck-mode' to be enabled" state) 593 | (when (and (bound-and-true-p flycheck-mode) 594 | (or flycheck-current-errors 595 | (eq 'running flycheck-last-status-change))) 596 | (let ((lighter (spaceline--flycheck-lighter ,state))) 597 | (when lighter (powerline-raw (s-trim lighter) ',face)))))))) 598 | 599 | (spaceline-define-segment evil-state 600 | "The current evil state. Requires `evil-mode' to be enabled." 601 | (when (bound-and-true-p evil-local-mode) 602 | (let ((tag (evil-state-property evil-state :tag t))) 603 | (s-trim (if (stringp tag) tag (funcall tag)))))) 604 | 605 | (defface spaceline-python-venv 606 | '((t (:foreground "plum1" :distant-foreground "DarkMagenta"))) 607 | "Face for highlighting the python venv." 608 | :group 'spaceline) 609 | 610 | (defvar spaceline-purpose-hide-if-not-dedicated nil 611 | "Hide the purpose segment if the window is not dedicated in 612 | some way.") 613 | 614 | (spaceline-define-segment purpose 615 | "The current window purpose. Requires `purpose-mode' to be 616 | enabled." 617 | (when (and (bound-and-true-p purpose-mode) 618 | (or (not spaceline-purpose-hide-if-not-dedicated) 619 | (purpose-window-purpose-dedicated-p) 620 | (window-dedicated-p))) 621 | (propertize (substring (purpose--modeline-string) 2 -1) 622 | 'face 'spaceline-python-venv 623 | 'help-echo "Window purpose"))) 624 | 625 | (spaceline-define-segment python-env 626 | "The current python env. Works with `pyvenv', `pyenv' and `conda'." 627 | (when (and active (eq 'python-mode major-mode)) 628 | (let (name source) 629 | (cond 630 | ((bound-and-true-p pyvenv-virtual-env-name) 631 | (setq name pyvenv-virtual-env-name source "pyvenv")) 632 | ((bound-and-true-p conda-env-current-name) 633 | (setq name conda-env-current-name source "conda")) 634 | ((and (fboundp 'pyenv-mode) (setq name (pyenv-mode-version))) 635 | (setq source "pyenv"))) 636 | (when name 637 | (propertize name 638 | 'face 'spaceline-python-venv 639 | 'help-echo (format "Virtual environment (via %s): %s" source name)))))) 640 | 641 | (spaceline-define-segment python-pyvenv 642 | "The current python venv. Works with `pyvenv'." 643 | (when (and active 644 | (eq 'python-mode major-mode) 645 | (bound-and-true-p pyvenv-virtual-env-name)) 646 | (propertize pyvenv-virtual-env-name 647 | 'face 'spaceline-python-venv 648 | 'help-echo (format "Virtual environment (via pyvenv): %s" 649 | pyvenv-virtual-env)))) 650 | 651 | (spaceline-define-segment python-pyenv 652 | "The current python venv. Works with `pyenv'." 653 | (when (and active 654 | (eq 'python-mode major-mode) 655 | (fboundp 'pyenv-mode-version) 656 | (pyenv-mode-version)) 657 | (let ((name (pyenv-mode-version))) 658 | (propertize name 659 | 'face 'spaceline-python-venv 660 | 'help-echo "Virtual environment (via pyenv)")))) 661 | 662 | (spaceline-define-segment paradox-menu 663 | "The current package info including upgradable, new, installed 664 | and total packages" 665 | (when (and active (derived-mode-p 'paradox-menu-mode)) 666 | mode-line-buffer-identification)) 667 | 668 | (spaceline-define-segment which-function 669 | (when (and active 670 | (bound-and-true-p which-function-mode) 671 | (bound-and-true-p which-func-mode)) 672 | (let* ((current (format-mode-line which-func-current))) 673 | (when (string-match "{\\(.*\\)}" current) 674 | (setq current (match-string 1 current))) 675 | (setq current (replace-regexp-in-string "%" "%%" current)) 676 | (propertize current 677 | 'local-map which-func-keymap 678 | 'face 'which-func 679 | 'mouse-face 'mode-line-highlight 680 | 'help-echo "mouse-1: go to beginning\n\ 681 | mouse-2: toggle rest visibility\n\ 682 | mouse-3: go to end")))) 683 | 684 | (spaceline-define-segment mu4e-alert-segment 685 | "Show the number of unread mails using mu. Requires mu4e-alert" 686 | (when (and active (featurep 'mu4e-alert)) 687 | mu4e-alert-mode-line) 688 | :global-override ((:eval mu4e-alert-mode-line))) 689 | 690 | (spaceline-define-segment recursive-edit 691 | "Shows the current recursive-edit depth." 692 | (format "↻%s" (recursion-depth)) 693 | :when (> (recursion-depth) 0)) 694 | 695 | (spaceline-define-segment macrodef 696 | "Shows when defining a keyboard macro." 697 | "•REC" 698 | :when defining-kbd-macro) 699 | 700 | (spaceline-define-segment treesit-inspect 701 | "Show tree-sitter node at point." 702 | (when (and active 703 | (fboundp 'treesit-available-p) 704 | (treesit-available-p) 705 | (bound-and-true-p treesit-inspect-mode)) 706 | '((:eval treesit--inspect-name)))) 707 | 708 | (spaceline-define-segment so-long 709 | "Show `so-long-mode-line-info'." 710 | (when (and active 711 | (bound-and-true-p so-long-mode-line-info)) 712 | '(("" so-long-mode-line-info)))) 713 | 714 | (provide 'spaceline-segments) 715 | 716 | ;;; spaceline-segments.el ends here 717 | -------------------------------------------------------------------------------- /spaceline.el: -------------------------------------------------------------------------------- 1 | ;;; spaceline.el --- Modeline configuration library for powerline 2 | 3 | ;; Copyright (C) 2015 TheBB 4 | ;; 5 | ;; Author: Eivind Fonn 6 | ;; URL: https://github.com/TheBB/spaceline 7 | ;; Version: 2.0.1 8 | ;; Keywords: mode-line powerline spacemacs 9 | ;; Package-Requires: ((emacs "24.4") (cl-lib "0.5") (powerline "2.3") (dash "2.11.0") (s "1.10.0")) 10 | 11 | ;; This file is not part of GNU Emacs. 12 | 13 | ;; This program is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation, either version 3 of the License, or 16 | ;; (at your option) any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this program. If not, see . 25 | 26 | ;;; Commentary: 27 | 28 | ;; Spaceline is a modular mode-line library built on the powerline package, 29 | ;; designed to make it easy to build beautiful mode-lines. 30 | ;; 31 | ;; It was originally used in the Spacemacs distribution, but has since been 32 | ;; extracted as a stand-alone package. 33 | 34 | ;;; Code: 35 | 36 | (require 'dash) 37 | (require 'gv) 38 | (require 'powerline) 39 | (require 'cl-lib) 40 | (require 'subr-x) 41 | 42 | (defvar evil-previous-state) 43 | (defvar evil-state) 44 | (defvar evil-visual-selection) 45 | 46 | (defvar spaceline-byte-compile t 47 | "Whether to byte compile the modeline.") 48 | 49 | (defvar spaceline-responsive t 50 | "If true, the modeline responds to narrow windows by 51 | dynamically hiding segments.") 52 | 53 | (defvar spaceline--mode-lines nil 54 | "Alist of modelines. 55 | Each CAR is a symbol naming the modeline, and the CDR is a cons 56 | cell (LEFT . RIGHT) where LEFT and RIGHT are lists of segments. 57 | See `spaceline-compile' for a description of segments.") 58 | 59 | (defvar spaceline-inflation nil 60 | "A factor by which to artificially pad the modeline. Note that 61 | this does not currently also impact the size of the powerline 62 | separators. Those must be set separately.") 63 | 64 | (defvar spaceline-pre-hook nil 65 | "Hook run before the modeline is rendered.") 66 | 67 | (defvar spaceline-separator-dir-left nil 68 | "The separator directions to use for the left side. 69 | Cons of the form (DIR . DIR) where DIR is one of left and right, 70 | or nil, in which case the best separators are chosen depending on 71 | the separator style.") 72 | 73 | (defvar spaceline-separator-dir-right nil 74 | "The separator directions to use for the right side. 75 | Cons of the form (DIR . DIR) where DIR is one of left and right, 76 | or nil, in which case the best separators are chosen depending on 77 | the separator style.") 78 | 79 | (defvar spaceline-directed-separators '(arrow arrow-fade brace butt curve roundstub utf-8) 80 | "List of directed powerline separators. 81 | Unless the directions are explicitly set in 82 | `spaceline-separator-dir-left' or 83 | `spaceline-separator-dir-right', these are the separators for 84 | which Spaceline will choose different directions on the left and 85 | right sides.") 86 | 87 | (defvar spaceline-highlight-face-func 'spaceline-highlight-face-default 88 | "The function that decides the highlight face. 89 | Superseded by `spaceline-face-func' if that variable is set.") 90 | 91 | (defvar spaceline-face-func nil 92 | "The function that defines all faces. 93 | Must be a function that accepts two arguments: FACE and ACTIVE, 94 | where FACE is `face1', `face2' `line' or `highlight', and ACTIVE 95 | determines whether the window in question is active. It should 96 | return a face to use. 97 | 98 | This variable supersedes `spaceline-highlight-face-func' if set.") 99 | 100 | (defvar spaceline-always-show-segments nil 101 | "When true, show all the segments that would otherwise be 102 | hidden in inactive windows.") 103 | 104 | (defun spaceline--get-separator-dirs (side) 105 | "Gets the preconfigured separator directions for SIDE, or the \"best\" ones, 106 | if not specified." 107 | (or (if (eq 'l side) 108 | spaceline-separator-dir-left 109 | spaceline-separator-dir-right) 110 | (cond 111 | ((memq powerline-default-separator spaceline-directed-separators) 112 | (if (eq 'l side) '(left . left) '(right . right))) 113 | (t '(left . right))))) 114 | 115 | (defun spaceline--get-face (face active) 116 | "Universal function to get the right face. 117 | FACE and ACTIVE have the same meanings as in 118 | `spaceline-face-func'. It delegates the work to 119 | `spaceline-face-func' if it is given, otherwise falls back to 120 | default configuration." 121 | (if spaceline-face-func 122 | (funcall spaceline-face-func face active) 123 | (cond 124 | ((eq 'face1 face) (if active 'powerline-active1 'powerline-inactive1)) 125 | ((eq 'face2 face) (if active 'mode-line 'mode-line-inactive)) 126 | ((eq 'line face) (if active 'powerline-active2 'powerline-inactive2)) 127 | ((eq 'highlight face) (if active 128 | (funcall spaceline-highlight-face-func) 129 | 'powerline-inactive1))))) 130 | 131 | (defface spaceline-highlight-face 132 | `((t (:background "DarkGoldenrod2" 133 | :foreground "#3E3D31" 134 | :inherit 'mode-line))) 135 | "Default highlight face for spaceline." 136 | :group 'spaceline) 137 | 138 | ;; Define various other highlight faces 139 | (dolist (s '((spaceline-evil-normal "DarkGoldenrod2" "Evil normal state face.") 140 | (spaceline-evil-insert "chartreuse3" "Evil insert state face.") 141 | (spaceline-evil-emacs "SkyBlue2" "Evil emacs state face.") 142 | (spaceline-evil-replace "chocolate" "Evil replace state face.") 143 | (spaceline-evil-visual "gray" "Evil visual state face.") 144 | (spaceline-evil-motion "plum3" "Evil motion state face.") 145 | (spaceline-unmodified "DarkGoldenrod2" "Unmodified buffer face.") 146 | (spaceline-modified "SkyBlue2" "Modified buffer face.") 147 | (spaceline-read-only "plum3" "Read-only buffer face."))) 148 | (eval `(defface ,(nth 0 s) 149 | `((t (:background ,(nth 1 s) 150 | :foreground "#3E3D31" 151 | :inherit 'mode-line))) 152 | ,(nth 2 s) 153 | :group 'spaceline))) 154 | 155 | (defun spaceline-highlight-face-default () 156 | "The default highlight face function. 157 | Set `spaceline-highlight-face-func' to 158 | `spaceline-highlight-face-default' to use this." 159 | 'spaceline-highlight-face) 160 | 161 | (defvar spaceline-evil-state-faces 162 | '((normal . spaceline-evil-normal) 163 | (insert . spaceline-evil-insert) 164 | (emacs . spaceline-evil-emacs) 165 | (replace . spaceline-evil-replace) 166 | (visual . spaceline-evil-visual) 167 | (motion . spaceline-evil-motion)) 168 | "Association list mapping evil states to their corresponding highlight faces. 169 | Is used by `spaceline-highlight-face-evil-state'.") 170 | 171 | (defun spaceline-highlight-face-evil-state () 172 | "Set the highlight face depending on the evil state. 173 | Set `spaceline-highlight-face-func' to 174 | `spaceline-highlight-face-evil-state' to use this." 175 | (if (bound-and-true-p evil-local-mode) 176 | (let* ((state (if (eq 'operator evil-state) evil-previous-state evil-state)) 177 | (face (assq state spaceline-evil-state-faces))) 178 | (if face (cdr face) (spaceline-highlight-face-default))) 179 | (spaceline-highlight-face-default))) 180 | 181 | (defun spaceline-highlight-face-modified () 182 | "Set the highlight face depending on the buffer modified status. 183 | Set `spaceline-highlight-face-func' to 184 | `spaceline-highlight-face-modified' to use this." 185 | (cond 186 | (buffer-read-only 'spaceline-read-only) 187 | ((buffer-modified-p) 'spaceline-modified) 188 | (t 'spaceline-unmodified))) 189 | 190 | (defun spaceline--imagep (object) 191 | "Test whether the given OBJECT is an image. 192 | An image is a list whose first element is the symbol `image'." 193 | (and (listp object) 194 | object 195 | (eq 'image (car object)))) 196 | 197 | (defun spaceline--intersperse (separator seq) 198 | "Intersperses SEPARATOR between each element of SEQ. 199 | This function does not run in-place. It returns a new list." 200 | (cond 201 | ((not seq) nil) 202 | ((not (cdr seq)) seq) 203 | (t (append (list (car seq) separator) 204 | (spaceline--intersperse separator (cdr seq)))))) 205 | 206 | (defun spaceline--mode-line-nonempty (seg) 207 | "Check whether a modeline segment SEG is nonempty." 208 | (let ((val (format-mode-line seg))) 209 | (cond ((listp val) val) 210 | ((stringp val) (< 0 (length val))) 211 | (t)))) 212 | 213 | (defmacro spaceline--parse-segment-spec (spec &rest body) 214 | "Destructure the segment specification SPEC and then run BODY. 215 | The following bindings are available in BODY: 216 | - `segment': The segment itself, either a symbol or a literal 217 | value, or a list of such. 218 | - `sym': The function that evaluates `segment', if it is a symbol. 219 | - `sym-form': The form that evaluates the segment, if it is a 220 | symbol. 221 | - `input-props': The property list part of SPEC, if present. 222 | - `props': The full property list (including those bound to the 223 | symbol, if applicable)." 224 | (declare (indent 1)) 225 | `(let* ((input (if (and (listp ,spec) 226 | (cdr ,spec) 227 | (keywordp (cadr ,spec))) 228 | ,spec 229 | (cons ,spec nil))) 230 | (segment (car input)) 231 | (sym (when (symbolp segment) (intern (format "spaceline-%s-p" segment)))) 232 | (sym-form (when (symbolp segment) (get sym :code))) 233 | (input-props (cdr input)) 234 | (props (append input-props 235 | (when (symbolp segment) (symbol-plist sym))))) 236 | ,@body)) 237 | 238 | (defun spaceline--gen-separator (face side) 239 | "Generate the code for producing a separator, if needed. 240 | Generates a separator with faces SEPARATOR-FACE (should be bound 241 | where the code runs) and FACE. SIDE is l or r." 242 | `((when needs-separator 243 | ,(if (eq 'l side) 244 | `(push (funcall default-sep separator-face ,face) result) 245 | `(push (funcall default-sep ,face separator-face) result)) 246 | (cl-rotatef default-sep other-sep) 247 | (setq needs-separator nil)))) 248 | 249 | (defun spaceline--gen-produce (face side) 250 | "Generate pre-production code. 251 | This code must run immediately before any segment produces 252 | output, if and only if it actually produces output. This will 253 | 1. Generate a separator with the correct FACE and SIDE. 254 | (see `spaceline--gen-separator') 255 | 2. Output the value of PRIOR, if given. 256 | 3. Reset the value of PRIOR to NEXT-PRIOR. 257 | 4. Set SEPARATOR-FACE for the next separator." 258 | `(,@(spaceline--gen-separator face side) 259 | (when prior 260 | (push prior result)) 261 | (setq prior next-prior) 262 | (setq separator-face ,face))) 263 | 264 | (defun spaceline--gen-segment (segment-spec side hidden &optional outer-props deep-or-fallback deep) 265 | "Generate the code for evaluating a segment. 266 | SEGMENT-SPEC is a valid Spaceline segment. See 267 | `spaceline-compile'. SIDE is either l or r. HIDDEN is a form 268 | that evaluates to true if the segment should be hidden, nil 269 | otherwise. OUTER-PROPS is a property list with properties 270 | inherited from parent segments. 271 | 272 | DEEP-OR-FALLBACK is nil if this segment is a top level segment or 273 | a fallback for a top level segment. 274 | 275 | DEEP is nil if and only if this segment is a top level segment. 276 | 277 | This function should only be called from outside code with 278 | OUTER-PROPS, DEEP-OR-FALLBACK and DEEP set to nil. 279 | 280 | Returns a list of forms." 281 | (spaceline--parse-segment-spec segment-spec 282 | (let* (;; Assemble the properties in the correct order 283 | (props (append props outer-props)) 284 | 285 | ;; Explicitly set the fallback property for child segments to nil, 286 | ;; as it should not be inherited 287 | (fallback (plist-get props :fallback)) 288 | (nest-props (append '(:fallback nil) input-props outer-props)) 289 | 290 | (condition (if (plist-member props :when) 291 | (plist-get props :when) t)) 292 | (face (or (plist-get props :face) 'default-face)) 293 | (face (if (memq face '(default-face other-face highlight-face line-face)) 294 | face `(,@face))) 295 | (separator `(powerline-raw ,(or (plist-get props :separator) " ") ,face)) 296 | (tight-left (or (plist-get props :tight) 297 | (plist-get props :tight-left))) 298 | (tight-right (or (plist-get props :tight) 299 | (plist-get props :tight-right))) 300 | 301 | ;; A temporary variable to check whether this segment has produced 302 | ;; output. It is uninterned to prevent it from clashing with those 303 | ;; of children or parent segments. 304 | (previous-result (make-symbol "spaceline--previous-result")) 305 | 306 | ;; A temporary variable to store the setting of `needs-separator', 307 | ;; in case we update it and need to change it back 308 | (previous-needs-separator (make-symbol "spaceline--previous-needs-separator")) 309 | 310 | ;; A temporary variable to store the setting of `separator-face', 311 | ;; in case we update it and need to change it back 312 | (previous-separator-face (make-symbol "spaceline--previous-separator-face")) 313 | 314 | clean-up-make 315 | clean-up-code) 316 | 317 | ;; On the right we output we produce output in the reverse direction, 318 | ;; so the meanings of left and right are interchanged 319 | (when (eq 'r side) (cl-rotatef tight-left tight-right)) 320 | 321 | ;; Clean-up-make and clean-up-code runs for top level segments that produce output 322 | ;; Anything that modifies `result' goes in clean-up-make 323 | (setq clean-up-make 324 | ;; Add padding unless the segment is tight 325 | (unless tight-right `((push (propertize " " 'face ,face) result)))) 326 | (setq clean-up-code 327 | `(;; Rotate the faces for the next top level segment 328 | ,@(unless (plist-get props :skip-alternate) 329 | '((cl-rotatef default-face other-face))) 330 | ;; We need a new separator at the next producing segment 331 | (setq needs-separator ,(not tight-right)))) 332 | 333 | `(;; Store the current result pointer in the temp variable 334 | (let ((,previous-result result) 335 | (,previous-needs-separator needs-separator) 336 | (,previous-separator-face separator-face)) 337 | 338 | ;; Don't produce a separator if the segment is tight 339 | ,@(when tight-left `((setq needs-separator nil))) 340 | 341 | ;; Top-level non-tight segments need padding 342 | ,@(unless (or deep-or-fallback tight-left) 343 | `((setq prior (propertize " " 'face ,face)))) 344 | 345 | ;; Evaluate the segment 346 | (when ,condition 347 | ,@(cond 348 | ((listp segment) 349 | ;; List segments can potentially have a new separator between 350 | ;; their elements, but not before the first one; therefore we 351 | ;; set NEXT-PRIOR but leave PRIOR alone 352 | `((let ((next-prior ,separator)) 353 | ,@(apply 'append 354 | (mapcar (lambda (s) 355 | (spaceline--gen-segment s side hidden nest-props 'deep-or-fallback 'deep)) 356 | (if (eq 'r side) (reverse segment) segment)))) 357 | ;; Since PRIOR may have been disrupted, we reset it here 358 | (setq prior next-prior))) 359 | ((symbolp segment) 360 | `((-when-let (value ,sym-form) 361 | ;; Symbol segments are assumed to not produce output unless 362 | ;; they evaluate to nil or an empty string 363 | (unless (and (stringp value) (string= "" value)) 364 | ,@(spaceline--gen-produce face side)) 365 | (cond 366 | ;; Images are lists, so they must be treated first 367 | ((spaceline--imagep value) (push value result)) 368 | ((listp value) 369 | (dolist (r ,(if (eq 'l side) 370 | `(spaceline--intersperse ,separator value) 371 | `(reverse (spaceline--intersperse ,separator value)))) 372 | (push (if (spaceline--imagep r) r (powerline-raw r ,face)) result))) 373 | ((and (stringp value) (string= "" value))) 374 | (t (push (powerline-raw value ,face) result)))))) 375 | (t 376 | ;; Literal segments are assumed to always produce; no check for 377 | ;; empty string here 378 | `(,@(spaceline--gen-produce face side) 379 | (push (powerline-raw (format "%s" ,segment) ,face) result))))) 380 | 381 | ;; If the segment failed to produce, but has a fallback, we call the fallback 382 | ,@(when fallback 383 | `((when (eq ,previous-result result) 384 | ,@(spaceline--gen-segment fallback side hidden nest-props deep-or-fallback 'deep)))) 385 | 386 | ;; Compute the length of the segment, and possibly run clean-up code 387 | ,@(unless deep 388 | `((if (eq ,previous-result result) 389 | ;; The segment didn't produce, so just set it to zero 390 | (setq segment-length 0) 391 | ;; The segment produced. First, do any clean-up that might alter the length 392 | ,@clean-up-make 393 | ;; Compute the length 394 | (let ((len (string-width (format-mode-line (powerline-render result))))) 395 | (setq segment-length (- len result-length)) 396 | ;; If the segment is hidden, forcibly reset the result pointer 397 | ;; If it's not hidden, update the result length and perform the rest of the cleanup 398 | (if ,hidden 399 | (setq result ,previous-result 400 | needs-separator ,previous-needs-separator 401 | separator-face ,previous-separator-face) 402 | ,@clean-up-code 403 | (setq result-length len))))))))))) 404 | 405 | (defun spaceline-compile (&rest args) 406 | "Compile a modeline. 407 | 408 | This function accepts a number of calling conventions: 409 | - With three arguments, TARGET, LEFT and RIGHT, it compiles a 410 | modeline named TARGET, with segment lists LEFT and RIGHT for 411 | the left and right sides respectively. 412 | - With two arguments, LEFT and RIGHT, the target takes the 413 | default value `main'. 414 | - With one argument, TARGET, it recompiles the modeline named 415 | TARGET with the same segments as it was originally compiled. 416 | - With no arguments, it recompiles all existing modelines with 417 | the same segments as they were originally compiled. 418 | 419 | In all cases, a function called `spaceline-ml-TARGET' is defined, 420 | which evaluates the modeline. It can then be used as a modeline 421 | by setting `mode-line-format' to 422 | 423 | (\"%e\" (:eval (spaceline-ml-TARGET))) 424 | 425 | If `spaceline-byte-compile' is non-nil, this function will be 426 | byte-compiled. This is recommended for regular usage as it 427 | improves performance significantly. 428 | 429 | If the segments are known statically at compile time, consider 430 | using `spaceline-generate' instead. 431 | 432 | Each element in LEFT and RIGHT must be a valid segment. Namely, 433 | - A literal string, integer or floating point number; or 434 | - a symbol, which has been defined with 435 | `spaceline-define-segment'; or 436 | - a list of segments; or 437 | - a list where the first element is a segment, and the rest of 438 | the list is a plist. 439 | 440 | The supported properties are 441 | - `:priority', a number representing the priority of appearance of that 442 | segment over the others, the higher the number the higher the priority. 443 | - `:when', a form that must evaluate to non-nil for the segment to 444 | show (default t) 445 | - `:face', the face with which to render the segment; may either 446 | one of the variables `default-face', `other-face' or `highlight-face' 447 | (default `default-face') or a form evaluating to a face. Thus any 448 | face symbol which is not either of the above three must be quoted. 449 | - `:separator', a string inserted between each element in a list 450 | segment (default \" \") 451 | - `:tight-left', non-nil if the segment should have no padding on 452 | the left side (default nil) 453 | - `:tight-right', non-nil if the segment should have no padding on 454 | the right side (default nil) 455 | - `:tight', non-nil if the segment should have no padding on 456 | either side (default nil) 457 | - `:fallback', another segment that will be called if no output 458 | is produced" 459 | (declare (indent defun)) 460 | (interactive) 461 | (if (not args) 462 | ;; Recompile all modelines 463 | (dolist (target spaceline--mode-lines) 464 | (spaceline-compile (car target))) 465 | (let* (;; Handle the different calling conventions 466 | (nargs (length args)) 467 | (target (if (cl-oddp nargs) (pop args) 'main)) 468 | (left-segs (if (> nargs 1) (pop args) 469 | (cadr (assq target spaceline--mode-lines)))) 470 | (right-segs (if (> nargs 1) (pop args) 471 | (cddr (assq target spaceline--mode-lines)))) 472 | (target-func (intern (format "spaceline-ml-%s" target)))) 473 | 474 | (eval (macroexpand-all `(spaceline-generate ,target ,left-segs ,right-segs))) 475 | 476 | (when spaceline-byte-compile 477 | (let ((byte-compile-warnings nil)) 478 | (byte-compile target-func))) 479 | 480 | ;; This is not strictly required, but it lets people use 481 | ;; `spaceline-compile' as an all-in-one fix-everything button 482 | (powerline-reset)))) 483 | 484 | (defalias 'spaceline-install 'spaceline-compile) 485 | 486 | (make-obsolete-variable 'spaceline-install 'spaceline-compile "2.0.2") 487 | 488 | (defmacro spaceline-generate (&rest args) 489 | "Compile a modeline. 490 | 491 | This is a macro-version of `spaceline-compile', useful for 492 | generating a modeline function when the segments are known 493 | statically at compile time. 494 | 495 | This macro accepts two calling conventions: 496 | - With three arguments, TARGET, LEFT and RIGHT, it compiles a 497 | modeline named TARGET, with segment lists LEFT and RIGHT for 498 | the left and right sides respectively. 499 | - With two arguments, LEFT and RIGHT, the target takes the 500 | default value `main'. 501 | 502 | In all cases, a function called `spaceline-ml-TARGET' is defined, 503 | which evaluates the modeline. It can then be used as a modeline 504 | by setting `mode-line-format' to 505 | 506 | (\"%e\" (:eval (spaceline-ml-TARGET))) 507 | 508 | See the documentation for `spaceline-compile' for how to specify 509 | LEFT and RIGHT." 510 | (declare (indent defun)) 511 | (let* (;; Handle the different calling conventions 512 | (nargs (length args)) 513 | (target (if (cl-oddp nargs) (pop args) 'main)) 514 | (left-segs (car args)) 515 | (right-segs (cadr args)) 516 | (target-func (intern (format "spaceline-ml-%s" target))) 517 | ;; Special support for the global segment: compile list of excludes 518 | (global-excludes (append (spaceline--global-excludes left-segs) 519 | (spaceline--global-excludes right-segs))) 520 | ;; Symbols for runtime data 521 | (left-symbol (intern (format "spaceline--segments-code-%s-left" target))) 522 | (right-symbol (intern (format "spaceline--segments-code-%s-right" target))) 523 | (priority-symbol (intern (format "spaceline--runtime-data-%s" target))) 524 | (left-code `(spaceline--code-for-side 525 | ,global-excludes ,left-symbol ,left-segs l)) 526 | (right-code `(spaceline--code-for-side 527 | ,global-excludes ,right-symbol ,right-segs r))) 528 | `(progn 529 | 530 | ;; Declare global runtime defaults 531 | (spaceline--declare-runtime ,left-segs ,right-segs ,left-symbol ,right-symbol ,priority-symbol) 532 | 533 | ;; Update stored segments so that recompilation will work 534 | (unless (assq ',target spaceline--mode-lines) 535 | (push '(,target) spaceline--mode-lines)) 536 | (setcdr (assq ',target spaceline--mode-lines) 537 | '(,left-segs . ,right-segs)) 538 | 539 | ;; Define the function that Emacs will call to generate the mode-line's 540 | ;; format string every time the mode-line is refreshed. 541 | (defun ,target-func () 542 | ;; Initialize the local runtime if necessary 543 | (unless ,priority-symbol 544 | (spaceline--init-runtime ,left-symbol ,right-symbol ,priority-symbol)) 545 | ;; Render the modeline 546 | (let ((fmt (spaceline--render-mode-line ,left-code ,right-code))) 547 | (and spaceline-responsive 548 | (spaceline--adjust-to-window ,priority-symbol fmt) 549 | (setq fmt (spaceline--render-mode-line ,left-code ,right-code))) 550 | fmt))))) 551 | 552 | (defmacro spaceline--code-for-side 553 | (global-excludes runtime-symbol segments side) 554 | "Generate the code that will evaluate all segments for one side. 555 | 556 | GLOBAL-EXCLUDES is used for the global segment, see `spaceline-define-segment'. 557 | 558 | RUNTIME-SYMBOL is a symbol storing the runtime data for this side, one 559 | three-element vector for each top-level segment, see 560 | `spaceline--declare-runtime' and `spaceline--init-runtime'. 561 | 562 | SEGMENTS is a list of segment specifications (see `spaceline--compile') and SIDE 563 | is either l or r, respectively for the left and the right side." 564 | (let ((sep-style (format "powerline-%s" powerline-default-separator)) 565 | (sep-dirs (spaceline--get-separator-dirs side))) 566 | `(let* ((default-face face1) 567 | (other-face face2) 568 | (default-sep ',(intern (format "%s-%s" sep-style (car sep-dirs)))) 569 | (other-sep ',(intern (format "%s-%s" sep-style (cdr sep-dirs)))) 570 | (global-excludes ',global-excludes) 571 | (result-length 0) 572 | (segment-length 0) 573 | (runtime-pointer ,runtime-symbol) 574 | prior 575 | next-prior 576 | needs-separator 577 | separator-face 578 | result) 579 | ,@(--map `(let ((runtime-data (pop runtime-pointer))) 580 | ,@(spaceline--gen-segment it side '(not (spaceline--shown runtime-data))) 581 | (spaceline--set-length runtime-data segment-length)) 582 | (if (eq 'l side) segments (reverse segments))) 583 | ,@(spaceline--gen-separator 'line-face side) 584 | ,(if (eq side 'l) '(reverse result) 'result)))) 585 | 586 | (defmacro spaceline--priority (val) `(aref ,val 0)) 587 | (defmacro spaceline--length (val) `(aref ,val 1)) 588 | (defmacro spaceline--shown (val) `(aref ,val 2)) 589 | (defmacro spaceline--set-length (vec val) `(aset ,vec 1 ,val)) 590 | (defmacro spaceline--set-shown (vec val) `(aset ,vec 2 ,val)) 591 | 592 | (defmacro spaceline--declare-runtime 593 | (segments-left segments-right left-symbol right-symbol priority-symbol) 594 | "Initialize the global runtime data for a modeline. 595 | 596 | The runtime consist of a three-element vector for each top-level 597 | segment in the modeline. The elements are: 598 | - priority: The priority of the segment (derived from its `:priority' property) 599 | - length: The rendered length of the segment 600 | - shown: Whether the segment is displayed or not 601 | 602 | The effect of this function is to create the default values for 603 | these vectors, and store them in the varibles LEFT-SYMBOL and 604 | RIGHT-SYMBOL, respectively, which are lists. The variable 605 | PRIORITY-SYMBOL is initialized with default value nil. 606 | 607 | See `spaceline--init-runtime' for more information." 608 | (let ((left (--map (spaceline--parse-segment-spec it 609 | (vector (or (plist-get props :priority) 0) 0 t)) 610 | segments-left)) 611 | (right (--map (spaceline--parse-segment-spec it 612 | (vector (or (plist-get props :priority) 0) 0 t)) 613 | segments-right))) 614 | `(progn 615 | ;; We use both defvar and setq so that recompilation will work. 616 | (defvar-local ,left-symbol nil "See `spaceline--declare-runtime'.") 617 | (setq-default ,left-symbol ',left) 618 | (defvar-local ,right-symbol nil "See `spaceline--declare-runtime'.") 619 | (setq-default ,right-symbol ',(reverse right)) 620 | (defvar-local ,priority-symbol nil "See `spaceline--declare-runtime'.") 621 | 622 | ;; Since we have possibly changed the defaults of these variables, kill 623 | ;; any local bindings that may exist. 624 | (dolist (buf (buffer-list)) 625 | (with-current-buffer buf 626 | (kill-local-variable ',left-symbol) 627 | (kill-local-variable ',right-symbol) 628 | (kill-local-variable ',priority-symbol)))))) 629 | 630 | (defmacro spaceline--render-mode-line (left-code right-code) 631 | "Call powerline to generate the mode-line format string. 632 | LEFT-CODE and RIGHT-CODE are the code that will be used " 633 | `(progn 634 | (run-hooks 'spaceline-pre-hook) 635 | (let* ((active-strict (powerline-selected-window-active)) 636 | (active (or spaceline-always-show-segments active-strict)) 637 | (line-face (spaceline--get-face 'line active-strict)) 638 | (highlight-face (spaceline--get-face 'highlight active-strict)) 639 | (face1 (spaceline--get-face 'face1 active-strict)) 640 | (face2 (spaceline--get-face 'face2 active-strict)) 641 | (lhs ,left-code) 642 | (rhs ,right-code)) 643 | (concat 644 | ,@(when spaceline-inflation 645 | `((propertize "\u200b" 'display 646 | '((raise ,(/ (1- spaceline-inflation) -2.0)) 647 | (height ,spaceline-inflation))))) 648 | (powerline-render lhs) 649 | (powerline-fill line-face (powerline-width rhs)) 650 | (powerline-render rhs))))) 651 | 652 | (defmacro spaceline--init-runtime (left-symbol right-symbol priority-symbol) 653 | "Initialize data structures used for the responsiveness of the modeline. 654 | 655 | This function 656 | - creates local deep copies of the global values of LEFT-SYMBOL and 657 | RIGHT-SYMBOL, 658 | - initializes PRIORITY-SYMBOl, a reordering of the same vectors whose order 659 | (by priority) decides the order of segments disappearance / reappearance with 660 | the size of the window. 661 | 662 | Note that the changes in the resulting PRIORITY-SYMBOL list are 663 | visible from LEFT-SYMBOL and RIGHT-SYMBOL, and vice versa. This 664 | creates a data structure that is efficiently accessible both in 665 | order of priority and order of segments." 666 | `(let ((left (--map (copy-tree it) (default-value ',left-symbol))) 667 | (right (--map (copy-tree it) (default-value ',right-symbol))) 668 | priority) 669 | (set (make-local-variable ',left-symbol) left) 670 | (set (make-local-variable ',right-symbol) right) 671 | (while (or left right) 672 | (when left (push (pop left) priority)) 673 | (when right (push (pop right) priority))) 674 | (set (make-local-variable ',priority-symbol) 675 | (sort priority 'spaceline--compare-priorities)))) 676 | 677 | (defmacro spaceline--adjust-to-window (responsiveness-runtime-data format) 678 | "Adjust the spaceline to the window by hiding or showing segments. 679 | 680 | RESPONSIVENESS-RUNTIME-DATA is a list of segments runtime data used to hide or 681 | show segments, see `spaceline--declare-runtime' for more info about 682 | how responsiveness works. 683 | 684 | FMT is the rendered modeline with the current visibility settings. 685 | 686 | Returns a truthy value if the visibility of any segment changed." 687 | ;; `1-' to not count the space generated by `powerline-fill' 688 | `(let ((total-length (1- (string-width (format-mode-line ,format)))) 689 | (width (+ (window-width) 690 | (or (cdr (window-margins)) 0) 691 | (or (car (window-margins)) 0))) 692 | changed) 693 | (if (> total-length width) 694 | ;; The modeline is too long, so try to hide some segments that are shown 695 | (let ((to-hide (--drop-while (not (spaceline--shown it)) 696 | ,responsiveness-runtime-data))) 697 | (--each-while to-hide 698 | (< width total-length) 699 | (cl-decf total-length (spaceline--length it)) 700 | (spaceline--set-shown it nil) 701 | (setq changed t))) 702 | ;; The modeline is shorter than it could be, so try to show some hidden segments 703 | (let ((to-show (--drop-while (spaceline--shown it) 704 | (reverse ,responsiveness-runtime-data)))) 705 | (--each-while to-show 706 | (> width (+ total-length (spaceline--length it))) 707 | (cl-incf total-length (spaceline--length it)) 708 | (spaceline--set-shown it t) 709 | (setq changed t)))) 710 | changed)) 711 | 712 | (defun spaceline--compare-priorities (first-alist second-alist) 713 | "Comparison predicate for sorting the segments runtime data by priority. 714 | Used as a predicate for `sort' in `spaceline--init-runtime'." 715 | (let ((first (spaceline--priority first-alist)) 716 | (second (spaceline--priority second-alist))) 717 | (< first second))) 718 | 719 | (defmacro spaceline-define-segment (name value &rest props) 720 | "Define a modeline segment called NAME with value VALUE and properties PROPS. 721 | 722 | Its value is computed by the form VALUE. The segment will not 723 | produce output if VALUE evaluates to nil or an empty string. All 724 | other values are assumed truthy. 725 | 726 | This macro defines a variable `spaceline--NAME-p' whose value can 727 | be used to switch the segment on or off. Its initial value is 728 | given by the optional keyword argument `:enabled', which defaults 729 | to true. 730 | 731 | If the segment is intended as a replacement for data which is 732 | otherwise inserted into `global-mode-string' (typically by 733 | another package), you can use the keyword argument 734 | `:global-override' to disable that. Its value is a single element 735 | or a list of elements which will be removed from 736 | `global-mode-string' before evaluation of the `global' segment. 737 | For modelines that do not use the `global' segment, this has no 738 | effect. 739 | 740 | All properties accepted in `spaceline-compile' are also accepted 741 | here. They are stored in a plist attached to the symbol 742 | `spaceline--NAME-p' to be inspected at compilation time by 743 | `spaceline-compile'. 744 | 745 | When a segment is redefined, the modelines must be recompiled for 746 | the changes to take effect." 747 | (declare (indent 1) 748 | (doc-string 2)) 749 | (let* ((wrapper-func (intern (format "spaceline--segment-%S" name))) 750 | (toggle-var (intern (format "spaceline-%S-p" name))) 751 | (toggle-func (intern (format "spaceline-toggle-%S" name))) 752 | (toggle-func-on (intern (format "spaceline-toggle-%S-on" name))) 753 | (toggle-func-off (intern (format "spaceline-toggle-%S-off" name))) 754 | (docstring (when (stringp value) 755 | (prog1 value 756 | (setq value (car props) 757 | props (cdr props))))) 758 | (value `(when ,toggle-var ,value)) 759 | (enabled (if (plist-member props :enabled) 760 | (plist-get props :enabled) 761 | t)) 762 | (global-override (plist-get props :global-override)) 763 | (global-override (if (listp global-override) 764 | global-override 765 | (list global-override)))) 766 | `(progn 767 | (defvar ,toggle-var ,enabled 768 | ,(format "True if modeline segment %S is enabled." name)) 769 | 770 | (defun ,toggle-func () (interactive) (setq ,toggle-var (not ,toggle-var))) 771 | (defun ,toggle-func-on () (interactive) (setq ,toggle-var t)) 772 | (defun ,toggle-func-off () (interactive) (setq ,toggle-var nil)) 773 | 774 | ;; Explicitly set the plist, in case the segment is redefined 775 | (let ((doc (get ',toggle-var 'variable-documentation))) 776 | (setplist ',toggle-var ',props) 777 | (put ',toggle-var 'variable-documentation doc)) 778 | 779 | ;; These properties must be explicitly set 780 | (put ',toggle-var :code ',value) 781 | (put ',toggle-var :global-override ',global-override)))) 782 | 783 | (defun spaceline--global-excludes (segments) 784 | "Compute global overrides from the segment list SEGMENTS." 785 | (let (excludes) 786 | (dolist (s-spec segments) 787 | (spaceline--parse-segment-spec s-spec 788 | (setq excludes (append (plist-get props :global-override) excludes)) 789 | (when (listp segment) 790 | (setq excludes (append (spaceline--global-excludes segment) excludes))))) 791 | excludes)) 792 | 793 | (spaceline-define-segment global 794 | (let ((global (if (listp global-mode-string) 795 | (cons "" (-difference global-mode-string global-excludes)) 796 | global-mode-string))) 797 | (when (spaceline--mode-line-nonempty global) 798 | (string-trim (powerline-raw global))))) 799 | 800 | (defun spaceline--string-trim-from-center (str len) 801 | "Return STR with its center chars trimmed for it to be a maximum length of LEN. 802 | When characters are trimmed, they are replaced with '...'." 803 | (if (> (length str) len) 804 | (let ((mid (/ (- len 3) 2))) 805 | (concat (substring str 0 mid) 806 | (apply #'propertize "..." (text-properties-at (- mid 1) str)) 807 | (substring str (- (1+ mid)) nil))) 808 | str)) 809 | 810 | (provide 'spaceline) 811 | 812 | ;;; spaceline.el ends here 813 | -------------------------------------------------------------------------------- /tests.el: -------------------------------------------------------------------------------- 1 | (ert-deftest spaceline-literal-segments-l () 2 | "Tests literal segments (left side)." 3 | (let ((powerline-default-separator 'utf-8) 4 | (powerline-utf-8-separator-left ?>)) 5 | (spaceline-install '("a" "b" 1.23 nil "longer" -2) nil) 6 | (should (string= " a > b > 1.23 > longer > -2 > " 7 | (spaceline-ml-main))))) 8 | 9 | (ert-deftest spaceline-list-segments-l () 10 | "Tests list segments (left side)." 11 | (let ((powerline-default-separator 'utf-8) 12 | (powerline-utf-8-separator-left ?>)) 13 | (spaceline-install '(("a" "b" "c") ("def" "ghj")) nil) 14 | (should (string= " a b c > def ghj > " 15 | (spaceline-ml-main))))) 16 | 17 | (ert-deftest spaceline-nested-list-segments-l () 18 | "Tests nested list segments (left side)." 19 | (let ((powerline-default-separator 'utf-8) 20 | (powerline-utf-8-separator-left ?>)) 21 | (spaceline-install '(("a" "b" "c") ("def" ("rofl" "mao" "sup") "ghj")) nil) 22 | (should (string= " a b c > def rofl mao sup ghj > " 23 | (spaceline-ml-main))))) 24 | 25 | (ert-deftest spaceline-symbol-l () 26 | "Tests symbols (left side)." 27 | (let ((powerline-default-separator 'utf-8) 28 | (powerline-utf-8-separator-left ?>)) 29 | (spaceline-define-segment a "" "a") 30 | (spaceline-define-segment b "" nil) 31 | (spaceline-define-segment c "" var) 32 | (spaceline-install '(a b) nil) 33 | (should (string= " a > " (spaceline-ml-main))) 34 | (spaceline-install '(a b c) nil) 35 | (let ((var "alpha")) 36 | (should (string= " a > alpha > " (spaceline-ml-main)))) 37 | (let ((var "beta")) 38 | (should (string= " a > beta > " (spaceline-ml-main)))))) 39 | 40 | (ert-deftest spaceline-list-separators-l () 41 | "Tests the :separator property of lists (left side)." 42 | (let ((powerline-default-separator 'utf-8) 43 | (powerline-utf-8-separator-left ?>)) 44 | (spaceline-install '((("a" "b" "c") :separator "~~")) nil) 45 | (should (string= " a~~b~~c > " (spaceline-ml-main))))) 46 | 47 | (ert-deftest spaceline-nested-list-separators-l () 48 | "Tests the :separator property of nested lists (left side)." 49 | (let ((powerline-default-separator 'utf-8) 50 | (powerline-utf-8-separator-left ?>)) 51 | (spaceline-install '((("a" (("b" "c" "d") :separator "$$") "e") :separator "~~")) nil) 52 | (should (string= " a~~b$$c$$d~~e > " (spaceline-ml-main))))) 53 | 54 | (ert-deftest spaceline-symbol-list-l () 55 | "Tests symbols returning lists, with the :separator property (left side)." 56 | (let ((powerline-default-separator 'utf-8) 57 | (powerline-utf-8-separator-left ?>)) 58 | (spaceline-define-segment a "" '("a" "b" "c")) 59 | (spaceline-define-segment b "" '("a" "b" "c") :separator "^") 60 | (spaceline-install '(a) nil) 61 | (should (string= " a b c > " (spaceline-ml-main))) 62 | (spaceline-install '((a :separator ".")) nil) 63 | (should (string= " a.b.c > " (spaceline-ml-main))) 64 | (spaceline-install '(b) nil) 65 | (should (string= " a^b^c > " (spaceline-ml-main))) 66 | (spaceline-install '((b :separator "!")) nil) 67 | (should (string= " a!b!c > " (spaceline-ml-main))))) 68 | 69 | (ert-deftest spaceline-fallback-literal-l () 70 | "Tests the :fallback property for literals (left side)." 71 | (let ((powerline-default-separator 'utf-8) 72 | (powerline-utf-8-separator-left ?>)) 73 | (spaceline-install '((nil :fallback "b")) nil) 74 | (should (string= " b > " (spaceline-ml-main))) 75 | (spaceline-install '(("a" :fallback "b")) nil) 76 | (should (string= " a > " (spaceline-ml-main))))) 77 | 78 | (ert-deftest spaceline-fallback-symbol-l () 79 | "Tests the :fallback property for symbols (left side)." 80 | (spaceline-define-segment success "" "success") 81 | (spaceline-define-segment failure "" nil) 82 | (spaceline-define-segment fallback "" nil :fallback success) 83 | (let ((powerline-default-separator 'utf-8) 84 | (powerline-utf-8-separator-left ?>)) 85 | (spaceline-install '((failure :fallback "b")) nil) 86 | (should (string= " b > " (spaceline-ml-main))) 87 | (spaceline-install '((success :fallback "b")) nil) 88 | (should (string= " success > " (spaceline-ml-main))) 89 | (spaceline-install '(fallback) nil) 90 | (should (string= " success > " (spaceline-ml-main))))) 91 | 92 | (ert-deftest spaceline-tight-l () 93 | "Tests the :tight properties (left side)." 94 | (let ((powerline-default-separator 'utf-8) 95 | (powerline-utf-8-separator-left ?>)) 96 | (spaceline-install '("a" ("b" :tight t) "c") nil) 97 | (should (string= " a b c > " (spaceline-ml-main))) 98 | (spaceline-install '("a" ("b" :tight-left t) "c") nil) 99 | (should (string= " a b > c > " (spaceline-ml-main))) 100 | (spaceline-install '("a" ("b" :tight-right t) "c") nil) 101 | (should (string= " a > b c > " (spaceline-ml-main))) 102 | (spaceline-define-segment tight "" "tight" :tight t) 103 | (spaceline-install '("a" tight "c") nil) 104 | (should (string= " a tight c > " (spaceline-ml-main))))) 105 | 106 | (ert-deftest spaceline-literal-segments-r () 107 | "Tests literal segments (right side)." 108 | (let ((powerline-default-separator 'utf-8) 109 | (powerline-utf-8-separator-right ?<)) 110 | (spaceline-install nil '("a" "b" 1.23 nil "longer" -2)) 111 | (should (string= " < a < b < 1.23 < longer < -2 " 112 | (spaceline-ml-main))))) 113 | 114 | (ert-deftest spaceline-list-segments-r () 115 | "Tests list segments (right side)." 116 | (let ((powerline-default-separator 'utf-8) 117 | (powerline-utf-8-separator-right ?<)) 118 | (spaceline-install nil '(("a" "b" "c") ("def" "ghj"))) 119 | (should (string= " < a b c < def ghj " 120 | (spaceline-ml-main))))) 121 | 122 | (ert-deftest spaceline-nested-list-segments-r () 123 | "Tests nested list segments (right side)." 124 | (let ((powerline-default-separator 'utf-8) 125 | (powerline-utf-8-separator-right ?<)) 126 | (spaceline-install nil '(("a" "b" "c") ("def" ("rofl" "mao" "sup") "ghj"))) 127 | (should (string= " < a b c < def rofl mao sup ghj " 128 | (spaceline-ml-main))))) 129 | 130 | (ert-deftest spaceline-symbol-r () 131 | "Tests symbols (right side)." 132 | (let ((powerline-default-separator 'utf-8) 133 | (powerline-utf-8-separator-right ?<)) 134 | (spaceline-define-segment a "" "a") 135 | (spaceline-define-segment b "" nil) 136 | (spaceline-define-segment c "" var) 137 | (spaceline-install nil '(a b)) 138 | (should (string= " < a " (spaceline-ml-main))) 139 | (spaceline-install nil '(a b c)) 140 | (let ((var "alpha")) 141 | (should (string= " < a < alpha " (spaceline-ml-main)))) 142 | (let ((var "beta")) 143 | (should (string= " < a < beta " (spaceline-ml-main)))))) 144 | 145 | (ert-deftest spaceline-list-separators-r () 146 | "Tests the :separator property of lists (right side)." 147 | (let ((powerline-default-separator 'utf-8) 148 | (powerline-utf-8-separator-right ?<)) 149 | (spaceline-install nil '((("a" "b" "c") :separator "~~"))) 150 | (should (string= " < a~~b~~c " (spaceline-ml-main))))) 151 | 152 | (ert-deftest spaceline-nested-list-separators-r () 153 | "Tests the :separator property of nested lists (right side)." 154 | (let ((powerline-default-separator 'utf-8) 155 | (powerline-utf-8-separator-right ?<)) 156 | (spaceline-install nil '((("a" (("b" "c" "d") :separator "$$") "e") :separator "~~"))) 157 | (should (string= " < a~~b$$c$$d~~e " (spaceline-ml-main))))) 158 | 159 | (ert-deftest spaceline-symbol-list-r () 160 | "Tests symbols returning lists, with the :separator property (right side)." 161 | (let ((powerline-default-separator 'utf-8) 162 | (powerline-utf-8-separator-right ?<)) 163 | (spaceline-define-segment a "" '("a" "b" "c")) 164 | (spaceline-define-segment b "" '("a" "b" "c") :separator "^") 165 | (spaceline-install nil '(a)) 166 | (should (string= " < a b c " (spaceline-ml-main))) 167 | (spaceline-install nil '((a :separator "."))) 168 | (should (string= " < a.b.c " (spaceline-ml-main))) 169 | (spaceline-install nil '(b)) 170 | (should (string= " < a^b^c " (spaceline-ml-main))) 171 | (spaceline-install nil '((b :separator "!"))) 172 | (should (string= " < a!b!c " (spaceline-ml-main))))) 173 | 174 | (ert-deftest spaceline-fallback-literal-r () 175 | "Tests the :fallback property for literals (right side)." 176 | (let ((powerline-default-separator 'utf-8) 177 | (powerline-utf-8-separator-right ?<)) 178 | (spaceline-install nil '((nil :fallback "b"))) 179 | (should (string= " < b " (spaceline-ml-main))) 180 | (spaceline-install nil '(("a" :fallback "b"))) 181 | (should (string= " < a " (spaceline-ml-main))))) 182 | 183 | (ert-deftest spaceline-fallback-symbol-r () 184 | "Tests the :fallback property for symbols (right side)." 185 | (spaceline-define-segment success "" "success") 186 | (spaceline-define-segment failure "" nil) 187 | (spaceline-define-segment fallback "" nil :fallback success) 188 | (let ((powerline-default-separator 'utf-8) 189 | (powerline-utf-8-separator-right ?<)) 190 | (spaceline-install nil '((failure :fallback "b"))) 191 | (should (string= " < b " (spaceline-ml-main))) 192 | (spaceline-install nil '((success :fallback "b"))) 193 | (should (string= " < success " (spaceline-ml-main))) 194 | (spaceline-install nil '(fallback)) 195 | (should (string= " < success " (spaceline-ml-main))))) 196 | 197 | (ert-deftest spaceline-tight-r () 198 | "Tests the :tight properties (right side)." 199 | (let ((powerline-default-separator 'utf-8) 200 | (powerline-utf-8-separator-right ?<)) 201 | (spaceline-install nil '("a" ("b" :tight t) "c")) 202 | (should (string= " < a b c " (spaceline-ml-main))) 203 | (spaceline-install nil '("a" ("b" :tight-left t) "c")) 204 | (should (string= " < a b < c " (spaceline-ml-main))) 205 | (spaceline-install nil '("a" ("b" :tight-right t) "c")) 206 | (should (string= " < a < b c " (spaceline-ml-main))) 207 | (spaceline-define-segment tight "" "tight" :tight t) 208 | (spaceline-install nil '("a" tight "c")) 209 | (should (string= " < a tight c " (spaceline-ml-main))))) 210 | 211 | (ert-deftest spaceline-recompile () 212 | "Tests recompilation." 213 | (let ((powerline-default-separator 'utf-8) 214 | (powerline-utf-8-separator-left ?>)) 215 | (spaceline-define-segment a "" "a") 216 | (spaceline-install '(a) nil) 217 | (should (string= " a > " (spaceline-ml-main))) 218 | (spaceline-define-segment a "" "b") 219 | (should (string= " a > " (spaceline-ml-main))) 220 | (spaceline-install) 221 | (should (string= " b > " (spaceline-ml-main))) 222 | (spaceline-define-segment a "" "a" :tight t) 223 | (should (string= " b > " (spaceline-ml-main))) 224 | (spaceline-install) 225 | (should (string= "a " (spaceline-ml-main))) 226 | (spaceline-define-segment a "" "c") 227 | (should (string= "a " (spaceline-ml-main))) 228 | (spaceline-install) 229 | (should (string= " c > " (spaceline-ml-main))))) 230 | --------------------------------------------------------------------------------