├── .gitignore ├── INTRODUCTION.md ├── LICENSE ├── README.md ├── autoload └── stargate.vim ├── doc └── stargate.txt ├── galaxy_labels ├── import └── stargate │ ├── galaxies.vim │ ├── messages.vim │ ├── stargates.vim │ ├── vim9000.vim │ └── workstation.vim └── test └── workstation.vim /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | -------------------------------------------------------------------------------- /INTRODUCTION.md: -------------------------------------------------------------------------------- 1 | ## The story 2 | 3 | The year is 3001. You are the only crew member of a fully automated science 4 | spaceship. Your only companion is advanced AI system VIM9000. The mission is to 5 | find a powerful obelisk, that can be anywhere in all known galaxies, so for 6 | fast traveling you are using technology called... STARGATES. 7 | 8 | ## Explained terminology of the source code 9 | 10 | **Ship** - current cursor position. 11 | 12 | **Galaxy** - window of vim, so all reachable galaxies are windows of the current 13 | tabpage. 14 | 15 | **Orbit** - a line of the buffer/window, so reachable are currently visible buffer 16 | lines. 17 | 18 | **Orbital arc** - visible columns of a line. 19 | 20 | **Degree** - a column of a line (I'm not an expert, so not sure if it is even 21 | correct term for an orbit). 22 | 23 | **Black matter** - vim folds. 24 | 25 | **Display** - current vim window without sign columns. 26 | 27 | **Labels** - hints for windows and cursor jump positions. 28 | 29 | **Destinations** - all possible cursor jumps for the char or a pattern. 30 | 31 | **Workstation** - collection of utility functions. 32 | 33 | **Star** - character on a line. 34 | 35 | **Stargate** - a hint with the desired position, act of using a stargate is just 36 | cursor jump to that hint. 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim9-stargate 2 | 3 | ![Stargate Presentation](https://raw.githubusercontent.com/monkoose/stargate-images/main/stargate_presentation.gif) 4 | 5 | You can think of **stargate** as simplified modern alternative to 6 | [easymotion](https://github.com/easymotion/vim-easymotion) for vim 8.2+. It 7 | uses [popups windows](https://vimhelp.org/popup.txt.html) to show hints, so it 8 | will not modify the content of your buffer and because of that linter plugins 9 | will not get mad. It is created for just one purpose - jump to any visible 10 | character in the current window without thinking of where your cursor is. 11 | Stargate do not support all the features of easymotion and will never do. 12 | 13 | ## Motivation 14 | 15 | There are few things that I miss in easymotion. First is already mentioned 16 | problem with linter plugins, and the second one is that if you are using 17 | non-English language in your code/text it makes harder to navigate in a buffer 18 | with easymotion, because jumping to non-English character requires switching 19 | your locale multiple times (first to choose this character, and second to go 20 | back to English and select a hint). Options section for `g:stargate_keymaps` 21 | describe how you can configure stargate to work with non-English text easier. 22 | 23 | ## Usage 24 | 25 | Stargate by default doesn't add any command or mapping. But at the same time it 26 | doesn't add anything to your vim startup time. So to use this plugin, you just 27 | need to create a mapping and call stargate function, like so 28 | ```vim 29 | " For 1 character to search before showing hints 30 | noremap f call stargate#OKvim(1) 31 | " For 2 consecutive characters to search 32 | noremap F call stargate#OKvim(2) 33 | 34 | " Instead of 1 or 2 you can use any higher number, but it isn't much practical 35 | " and it is easier to use `/` or `?` for that 36 | ``` 37 | **Notice** that it is `noremap` (not `nnoremap`) this allows stargate to work not 38 | only in normal mode, but also in visual and operator-pending modes. Do not use 39 | `:call ...` (`call ...` is required) or plugin will behave not as you want 40 | it to. 41 | 42 | To change current window when stargate is enabled (but not in a hints 43 | mode) just press ``, so then you can choose window label to swap to it 44 | (`space` to return to the current window). If for some reason you want to use 45 | this feature outside of stargate plugin itself you can map provided function 46 | to some convenient key 47 | ```vim 48 | nnoremap w call stargate#Galaxy() 49 | ``` 50 | And here we actually use `nnoremap` this time , because it makes no sense to 51 | swap to another window in visual or operator-pending modes. 52 | 53 | To exit stargate at any moment press `` or ``. 54 | 55 | If you want to use another jump locations like easymotion jump to start of a 56 | word, or start of a line etc. You can use `stargate#OKvim()` with a string as 57 | its only argument. This string is just some vim regexp. 58 | ```vim 59 | " for the start of a word 60 | noremap w call stargate#OKvim('\<') 61 | " for the end of a word 62 | noremap e call stargate#OKvim('\S\>') 63 | " for the start of a line 64 | noremap l call stargate#OKvim('\_^') 65 | " for the last character on the line 66 | noremap E call stargate#OKvim('\S\s*$') 67 | " for the end of a line 68 | noremap $ call stargate#OKvim('$') 69 | " for any bracket, parentheses or curly bracket 70 | noremap [ call stargate#OKvim('[(){}[\]]') 71 | ``` 72 | As you can see possible jump locations are limited only by your knowledge of 73 | vim regexp. **Note** make sure to use single quotes (literal strings) or you 74 | would need to escape every backslash in a pattern. 75 | 76 | ## Configuration 77 | 78 | ### Options 79 | 80 | | Variable | Description | Default | 81 | |-------------------------|-------------------------------------------|---------------------| 82 | | `g:stargate_ignorecase` | Ignore case of the search. | `v:true` | 83 | | `g:stargate_limit` | Maximum number of popups.1 | `300` | 84 | | `g:stargate_chars` | Chars used for hints. | `'fjdklshgaewiomc'` | 85 | | `g:stargate_name` | How should VIM9000 call you. | `'Human'` | 86 | | `g:stargate_keymaps` | Dict of all possible keymaps.2 | `{}` | 87 | 88 | **1** - This limit is required, because spawning a lot of popups is slow in vim. You 89 | can increase it if you have found that it sometimes limits your search 90 | results, but for any practical usage default value is enough. 91 | 92 | **2** - It is a dictionary of the keys - characters that you press and values - 93 | all characters you want to include in a search besides the key character. Both 94 | are strings. As an example for Russian language it can look like this 95 | ```vim 96 | let g:stargate_keymaps = { 97 | \ "~": "Ё", 98 | \ "Q": "Й", "W": "Ц", "E": "У", "R": "К", "T": "Е", "Y": "Н", "U": "Г", "I": "Ш", "O": "Щ", "P": "З", "{": "Х", "}": "Ъ", 99 | \ "A": "Ф", "S": "Ы", "D": "В", "F": "А", "G": "П", "H": "Р", "J": "О", "K": "Л", "L": "Д", ":": "Ж", '"': "Э", 100 | \ "Z": "Я", "X": "Ч", "C": "С", "V": "М", "B": "И", "N": "Т", "M": "Ь", "<": "Б", ">": "Ю", 101 | \ "`": "ё", 102 | \ "q": "й", "w": "ц", "e": "у", "r": "к", "t": "е", "y": "н", "u": "г", "i": "ш", "o": "щ", "p": "з", "[": "х", "]": "ъ", 103 | \ "a": "ф", "s": "ы", "d": "в", "f": "а", "g": "п", "h": "р", "j": "о", "k": "л", "l": "д", ";": "ж", "'": "э", 104 | \ "z": "я", "x": "ч", "c": "с", "v": "м", "b": "и", "n": "т", "m": "ь", ",": "б", ".": "ю" 105 | \ } 106 | ``` 107 | You can add as many chars in a string as you want, and all of them will be 108 | searched for that dictionary key. As example to search for `t`, `е` (it's 109 | Russian е) and `ё` with only `t` search 110 | ```vim 111 | let g:stargate_keymaps = { "t": "её" } 112 | ``` 113 | Or to jump to any of bracket, parentheses or curly bracket on `[` search 114 | ```vim 115 | let g:stargate_keymaps = { "[": "[](){}" } 116 | ``` 117 | 118 | ### Colors 119 | 120 | Stargate provides some highlight groups that you can change to look good with 121 | your color scheme. 122 | 123 | | Highlight group | Description | 124 | |-----------------------|-----------------------------------------------------------| 125 | | StargateFocus | visible text of the current window when stargate invoked | 126 | | StargateDesaturate | visible text when hints are enabled | 127 | | StargateError | text highlight when something goes wrong | 128 | | StargateLabels | window labels | 129 | | StargateErrorLabels | window labels when something goes wrong | 130 | | StargateMain | main color of the hints | 131 | | StargateSecondary | secondary colors of the hints | 132 | | StargateShip | highlight for cursor position | 133 | | StargateVIM9000 | color for VIM9000 name in the command line | 134 | | StargateMessage | color of the standard message from VIM9000 | 135 | | StargateErrorMessage | color of the error message from VIM9000 | 136 | | StargateVisual | color of the visual selection when stargate is active | 137 | 138 | Defaults are 139 | ```vim 140 | highlight default StargateFocus guifg=#958c6a 141 | highlight default StargateDesaturate guifg=#49423f 142 | highlight default StargateError guifg=#d35b4b 143 | highlight default StargateLabels guifg=#caa247 guibg=#171e2c 144 | highlight default StargateErrorLabels guifg=#caa247 guibg=#551414 145 | highlight default StargateMain guifg=#f2119c gui=bold cterm=bold 146 | highlight default StargateSecondary guifg=#11eb9c gui=bold cterm=bold 147 | highlight default StargateShip guifg=#111111 guibg=#caa247 148 | highlight default StargateVIM9000 guifg=#111111 guibg=#b2809f gui=bold cterm=bold 149 | highlight default StargateMessage guifg=#a5b844 150 | highlight default StargateErrorMessage guifg=#e36659 151 | highlight default link StargateVisual Visual 152 | ``` 153 | 154 | Remove `default` from this list and add any highlight group you want to change 155 | into your vimrc. 156 | 157 | Change StargateVisual when visual selection highlight of the current 158 | colorscheme has bad contrast with other stargate colors. 159 | 160 | ## FAQ 161 | 162 | - **What are this weird naming in the source code?** 163 | 164 | Just for fun 165 | [INTRODUCTION](https://github.com/monkoose/vim9-stargate/blob/main/INTRODUCTION.md) 166 | should explain it a little bit. 167 | 168 | ## TODO 169 | 170 | - [x] Add vim documentation 171 | - [ ] Add tests 172 | - [ ] Improve text grammar 173 | -------------------------------------------------------------------------------- /autoload/stargate.vim: -------------------------------------------------------------------------------- 1 | vim9script 2 | 3 | import '../import/stargate/workstation.vim' as ws 4 | import '../import/stargate/vim9000.vim' as vim 5 | import '../import/stargate/galaxies.vim' 6 | 7 | g:stargate_ignorecase = get(g:, 'stargate_ignorecase', true) 8 | g:stargate_limit = get(g:, 'stargate_limit', 300) 9 | g:stargate_chars = get(g:, 'stargate_chars', 'fjdklshgaewiomc')->split('\zs') 10 | g:stargate_name = get(g:, 'stargate_name', 'Human') 11 | g:stargate_keymaps = get(g:, 'stargate_keymaps', {}) 12 | 13 | # Initialize highlights 14 | ws.CreateHighlights() 15 | 16 | # Apply highlights on a colorscheme change 17 | augroup StargateReapplyHighlights 18 | autocmd! 19 | autocmd ColorScheme * ws.CreateHighlights() 20 | augroup END 21 | 22 | # Initialize hidden popup windows for stargates hints 23 | ws.CreateLabelWindows() 24 | 25 | 26 | # Public API functions 27 | export def OKvim(mode: any) 28 | vim.OkVIM(mode) 29 | enddef 30 | 31 | export def Galaxy() 32 | galaxies.ChangeGalaxy(true) 33 | enddef 34 | 35 | # vim: sw=4 36 | -------------------------------------------------------------------------------- /doc/stargate.txt: -------------------------------------------------------------------------------- 1 | *stargate.txt* modern alternative to easymotion plugin 2 | 3 | Author: monkoose 4 | License: The Unlicense 5 | 6 | ============================================================================== 7 | CONTENTS *stargate-contents* 8 | 9 | Introduction |stargate-introduction| 10 | Usage |stargate-usage| 11 | Configuration |stargate-configuration| 12 | Highlights |stargate-highlight-groups| 13 | 14 | ============================================================================== 15 | INTRODUCTION *stargate-introduction* 16 | 17 | Stargate allows to jump to a visible character on the screen with lesser 18 | keystrokes. It is modern and simplified alternative to easymotion plugin. 19 | 20 | ============================================================================== 21 | USAGE *stargate-usage* 22 | 23 | Stargate provides two public functions that you can map to any key to use it. 24 | 25 | stargate#OKvim({mode}) *stargate#OKvim()* 26 | 27 | {mode} is a unsigned number or a string. When it is number stargate 28 | waits for the user to type as much consecutive characters and then 29 | search for this characters in the current window. 30 | > 31 | " For 1 character to search before showing hints 32 | noremap f call stargate#OKvim(1) 33 | " For 2 consecutive characters to search 34 | noremap F call stargate#OKvim(2) 35 | < 36 | Instead of 1 or 2 you can use any higher nubmer, but it isn't that 37 | much practical and it is easier to use |/| and |?| for such search. 38 | 39 | When {mode} is a string it processed as just vim regexp for search in 40 | the current window. 41 | > 42 | " for the start of a word 43 | noremap w call stargate#OKvim('\<') 44 | " for the end of a word 45 | noremap e call stargate#OKvim('\S\>') 46 | " for the start of a line 47 | noremap l call stargate#OKvim('\_^') 48 | " for the last character on the line 49 | noremap E call stargate#OKvim('\S\s*$') 50 | " for the end of a line 51 | noremap $ call stargate#OKvim('$') 52 | " for any bracket, parentheses or curly bracket 53 | noremap [ call stargate#OKvim('\[(){}[\]]') 54 | < 55 | As you can see from this examples possible jump locations are limited 56 | only by your knowledge of vim |regexp|. 57 | 58 | Notice that |noremap| is used (do not mix up it with |nnoremap|). 59 | With |noremap| you can use stargate not only in normal, but also in 60 | visual and operator-pending modes. 61 | Also notice that ||call is used instead of `:call ...`. It is 62 | required, without it the plugin in visual mode will behave not as 63 | expected. 64 | 65 | stargate#Galaxy() *stargate#Galaxy()* 66 | 67 | Used to invoke window switching mode of the stargate plugin outside of 68 | the main function. 69 | > 70 | nnoremap w call stargate#Galaxy() 71 | < 72 | Notice that for this function |nnoremap| is used this time because 73 | there is no sense to change window in visual or operator-pending 74 | modes. 75 | 76 | To change current window when |stargate#OKvim()| was called you can press 77 | `` so |stargate#Galaxy()| will be invoked. 78 | 79 | To exit stargate at any moment hit `` or ``. 80 | 81 | ============================================================================== 82 | CONFIGURATION *stargate-configuration* 83 | 84 | g:stargate_ignorecase *g:stargate_ignorecase* 85 | 86 | Should searches ignore case of characters. 87 | `Default is v:true` 88 | 89 | g:stargate_limit *g:stargate_limit* 90 | 91 | Maximum number of popups that can be showed for the hints. This limit 92 | is required, because spawning a lot of popups is slow in vim. You can 93 | increase it if you have found that it sometimes limits your search 94 | results, but for any practical usage default value is enough. If your 95 | search exceeds this limit the error "Vim:stargate: too much popups to 96 | show - (number of popups)" will be shown in echo area. 97 | `Default is 300` 98 | 99 | g:stargate_chars *g:stargate_chars* 100 | 101 | Characters used to display hints. 102 | `Default is 'fjdklshgaewiomc'` 103 | 104 | g:stargate_name *g:stargate_name* 105 | 106 | How fictional AI VIM9000 should call you. 107 | `Default is 'Human'` 108 | 109 | g:stargate_keymaps *g:stargate_keymaps* 110 | 111 | Dictionary where |keys| are keyboard character that you press in 112 | current locale and |values| (string) are all characters you want to 113 | include in a search beside the key character. 114 | As example for Russian language 115 | > 116 | let g:stargate_keymaps = { 117 | \ "~": "Ё", 118 | \ "Q": "Й", "W": "Ц", "E": "У", "R": "К", "T": "Е", "Y": "Н", "U": "Г", "I": "Ш", "O": "Щ", "P": "З", "{": "Х", "}": "Ъ", 119 | \ "A": "Ф", "S": "Ы", "D": "В", "F": "А", "G": "П", "H": "Р", "J": "О", "K": "Л", "L": "Д", ":": "Ж", '"': "Э", 120 | \ "Z": "Я", "X": "Ч", "C": "С", "V": "М", "B": "И", "N": "Т", "M": "Ь", "<": "Б", ">": "Ю", 121 | \ "`": "ё", 122 | \ "q": "й", "w": "ц", "e": "у", "r": "к", "t": "е", "y": "н", "u": "г", "i": "ш", "o": "щ", "p": "з", "[": "х", "]": "ъ", 123 | \ "a": "ф", "s": "ы", "d": "в", "f": "а", "g": "п", "h": "р", "j": "о", "k": "л", "l": "д", ";": "ж", "'": "э", 124 | \ "z": "я", "x": "ч", "c": "с", "v": "м", "b": "и", "n": "т", "m": "ь", ",": "б", ".": "ю" 125 | \ } 126 | < 127 | `Default is {}` 128 | 129 | ------------------------------------------------------------------------------ 130 | HIGHLIGHTS *stargate-highlight-groups* 131 | 132 | Stargate provides some highlight groups that can be used to improve feel and 133 | look with any color scheme. 134 | 135 | StargateFocus *StargateFocus* 136 | 137 | Text highlight for a visible text in the current window when stargate 138 | plugin is invoked. Makes it easiear to track which window is currently 139 | selected and notify the user that stargate plugin have started. 140 | `Default is guifg=#958c6a` 141 | 142 | StargateDesaturate *StargateDesaturate* 143 | 144 | Text highlight for a visible text in the current window when hints are 145 | shown. Makes it easier to read hint labels. 146 | `Default is guifg=#49423f` 147 | 148 | StargateError *StargateError* 149 | 150 | Text highlight for a visible text in the current window when error 151 | invoked (no searched pattern in the current window or wrong hint 152 | character pressed). 153 | `Default is guifg=#d35b4b` 154 | 155 | StargateLabels *StargateLabels* 156 | 157 | Hihglight for labels when your are in window switching mode. 158 | `Default is guifg=#caa247 guibg=#171e2c` 159 | 160 | StargateErrorLabels *StargateErrorLabels* 161 | 162 | Highlight for labels when your are in window switching mode and 163 | pressed wrong label character. 164 | `Default is guifg=#caa247 guibg=#551414` 165 | 166 | StargateMain *StargateMain* 167 | 168 | Default highlight for the hints. 169 | `Default is guifg=#f2119c gui=bold cterm=bold` 170 | 171 | StargateSecondary *StargateSecondary* 172 | 173 | Highlight for hints that are close to another one. 174 | `Default is guifg=#11eb9c gui=bold cterm=bold` 175 | 176 | StargateShip *StargateShip* 177 | 178 | Highlight for cursor position in the current window. 179 | `Default is guifg=#111111 guibg=#caa247` 180 | 181 | StargateVIM9000 *StargateVIM9000* 182 | 183 | Highlight for fictional AI companion name that shown in the 184 | command-line. 185 | `Default is guifg=#111111 guibg=#b2809f gui=bold cterm=bold` 186 | 187 | StargateMessage *StargateMessage* 188 | 189 | Highlight for a standard message from VIM9000. 190 | `Default is guifg=#a5b844` 191 | 192 | StargateErrorMessage *StargateErrorMessage* 193 | 194 | Highlight for a message from VIM9000 when error invoked. 195 | `Default is guifg=#e36659` 196 | 197 | StargateVisual *StargateVisual* 198 | 199 | Highlight for visual selection when stargate is active. 200 | Change it if visual selection of the current colorscheme has bad 201 | contrast with other stargate highlights. 202 | `Default links to Visual` 203 | 204 | ============================================================================== 205 | vim:tw=78:ft=help:norl:fen: 206 | -------------------------------------------------------------------------------- /galaxy_labels: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ▀▀▀▀▀ 5 | ▄▄▄▄▄ 6 | █▂▂▂ 7 | █▔▔▔ 8 | ▀ 9 | ▄▄▄ 10 | █ 11 | ▄ █ 12 | ▔▀▀▀▀ 13 | ▄▄▄▄ 14 | █ █ 15 | █ █ 16 | ▀▀▀▀ 17 | ▄ 18 | █ 19 | █ ▄ 20 | ▀▀▀▀▀ 21 | ▄▄▄▄▄ 22 | █▂▂▂▂ 23 | ▔▔▔▔█ 24 | ▀▀▀▀▀ 25 | ▄ ▄ 26 | █▂▂▂█ 27 | █▔▔▔█ 28 | ▀ ▀ 29 | ▄▄▄▄ 30 | █ ▁▁ 31 | █ ▔█ 32 | ▀▀▀▀▀ 33 | ▄▄▄ 34 | █ █ 35 | █▀▀▀█ 36 | ▀ ▀ 37 | ▄▄▄ 38 | █ 39 | █ 40 | ▀▀▀ 41 | ▄▄▄▄▄ 42 | █▂▂▂ 43 | █▔▔▔ 44 | ▀▀▀▀▀ 45 | ▄▄▄ 46 | █ █ 47 | █ █ 48 | ▀▀▀ 49 | ▄▄▄▄ 50 | █ 51 | █ 52 | ▀▀▀▀ 53 | ▄ ▄ 54 | █ █ 55 | █ █ 56 | ▀▀▀▀ 57 | -------------------------------------------------------------------------------- /import/stargate/galaxies.vim: -------------------------------------------------------------------------------- 1 | vim9script 2 | 3 | import './messages.vim' as msg 4 | import './workstation.vim' as ws 5 | 6 | const labels = [' ', 'f', 'j', 'd', 'l', 's', 'h', 'g', 'a', 'i', 'e', 'o', 'c', 'u'] 7 | const galaxy_labels = expand(':p:h:h:h') .. '/galaxy_labels' 8 | 9 | 10 | # Swap current window info into 0 index of `wininfo` parameter 11 | def CurrentGalaxyToFirst(wininfo: list>) 12 | const cur_winnr = winnr() 13 | for i in range(len(wininfo)) 14 | if wininfo[i].winnr == cur_winnr 15 | wininfo->insert(wininfo->remove(i)) 16 | endif 17 | endfor 18 | enddef 19 | 20 | 21 | # Create popup in the center of the window from `wininfo` parameter 22 | def GalaxyLabel(buf_nr: number, line_nr: number, wininfo: dict): number 23 | const width = 7 24 | const height = 4 25 | const row = wininfo.winrow + wininfo.height / 2 - height / 2 26 | const column = wininfo.wincol + wininfo.width / 2 - width / 2 27 | return popup_create( buf_nr, { 28 | line: row, 29 | col: column, 30 | flip: false, 31 | minheight: height, 32 | maxheight: height, 33 | minwidth: width, 34 | maxwidth: width, 35 | firstline: line_nr, 36 | wrap: false, 37 | drag: false, 38 | resize: false, 39 | highlight: 'StargateLabels', 40 | scrollbar: false, 41 | zindex: 10000, 42 | moved: 'any' 43 | }) 44 | enddef 45 | 46 | 47 | # Creates popups with labels for all windows in the current tabpage 48 | def DisplayGalaxiesLabels(buf_nr: number, wininfo: list>): dict 49 | var galaxies = {} 50 | for i in range(len(wininfo)) 51 | const id = GalaxyLabel(buf_nr, i * 4 + 1, wininfo[i]) 52 | galaxies[labels[i]] = { popupid: id, winid: wininfo[i].winid } 53 | endfor 54 | return galaxies 55 | enddef 56 | 57 | 58 | def LabelsError(gal: dict) 59 | const galaxies = values(gal) 60 | def Recolor(highlight: string) 61 | for galaxy in galaxies 62 | popup_setoptions(galaxy.popupid, { highlight: highlight }) 63 | endfor 64 | msg.ErrorMessage($"Our ship can't reach that galaxy, {g:stargate_name}") 65 | enddef 66 | 67 | timer_start(5, (t) => Recolor('StargateErrorLabels')) 68 | timer_start(150, (t) => Recolor('StargateLabels')) 69 | enddef 70 | 71 | 72 | # Switch to the labeled window and return 1, on error or returns 0 73 | def InputLoop(galaxies: dict>, independent: bool): number 74 | while true 75 | const [nr: number, err: bool] = ws.SafeGetChar() 76 | 77 | if err || nr == 27 78 | return 0 79 | endif 80 | 81 | const char = nr2char(nr) 82 | const destination = copy(galaxies)->filter((k, _) => k == char) 83 | if empty(destination) 84 | LabelsError(galaxies) 85 | continue 86 | endif 87 | 88 | if !independent 89 | ws.ClearScreen() 90 | endif 91 | win_gotoid(destination[char].winid) 92 | if !independent 93 | ws.UpdateWinBounds() 94 | ws.SetScreen() 95 | endif 96 | break 97 | endwhile 98 | return 1 99 | enddef 100 | 101 | 102 | export def ChangeGalaxy(independent: bool): number 103 | if !filereadable(galaxy_labels) 104 | msg.ErrorMessage("Internal error, can't display galaxies.") 105 | return 0 106 | endif 107 | 108 | const tabnr = tabpagenr() 109 | var galaxies_info = getwininfo()->filter((_, v) => v.tabnr == tabnr) 110 | if len(galaxies_info) == 1 111 | # when starts with stargate#Galaxy() call we need to set g variables 112 | # to highlight that range on error 113 | if independent 114 | ws.UpdateWinBounds() 115 | endif 116 | msg.Error($"{g:stargate_name}, your species can't outsmart me.") 117 | return 1 118 | endif 119 | 120 | const buf_nr = bufadd('galaxy_labels') 121 | setbufvar(buf_nr, '&buftype', 'popup') 122 | bufload(buf_nr) 123 | readfile(galaxy_labels)->setbufline(buf_nr, 1) 124 | CurrentGalaxyToFirst(galaxies_info) 125 | const galaxies = DisplayGalaxiesLabels(buf_nr, galaxies_info) 126 | var result: number 127 | 128 | if independent 129 | ws.HideCursor() 130 | endif 131 | 132 | msg.StandardMessage($'Choose a galaxy for the hyperjump, {g:stargate_name}.') 133 | result = InputLoop(galaxies, independent) 134 | for galaxy in values(galaxies) 135 | popup_close(galaxy.popupid) 136 | endfor 137 | 138 | if independent 139 | ws.ShowCursor() 140 | endif 141 | 142 | if !independent && result 143 | msg.StandardMessage('Now choose a destination.') 144 | else 145 | msg.BlankMessage() 146 | endif 147 | 148 | return result 149 | enddef 150 | 151 | defcompile 152 | 153 | # vim: sw=4 154 | -------------------------------------------------------------------------------- /import/stargate/messages.vim: -------------------------------------------------------------------------------- 1 | vim9script 2 | 3 | import './workstation.vim' as ws 4 | 5 | 6 | def Message(text: string, color: string) 7 | redraw 8 | exe 'echohl StargateVIM9000' 9 | echo ' VIM9000 ' 10 | exe $'echohl {color}' 11 | echon $' {text} ' 12 | echohl None 13 | enddef 14 | 15 | 16 | export def ErrorMessage(text: string) 17 | Message(text, 'StargateErrorMessage') 18 | enddef 19 | 20 | 21 | export def StandardMessage(text: string) 22 | Message(text, 'StargateMessage') 23 | enddef 24 | 25 | 26 | export def Error(message: string) 27 | def RemoveError(t: number) 28 | ws.RemoveMatchHighlight(ws.win['StargateError']) 29 | # redraw required, because while getchar() is active 30 | # the screen is not redrawn normally 31 | redraw 32 | enddef 33 | 34 | ws.AddMatchHighlight('StargateError', 1002) 35 | ErrorMessage(message) 36 | timer_start(150, RemoveError) 37 | enddef 38 | 39 | 40 | export def Warning(message: string) 41 | redraw 42 | echohl WarningMsg 43 | echom message 44 | echohl None 45 | enddef 46 | 47 | 48 | export def BlankMessage() 49 | redraw 50 | echo '' 51 | enddef 52 | 53 | # vim: sw=4 54 | -------------------------------------------------------------------------------- /import/stargate/stargates.vim: -------------------------------------------------------------------------------- 1 | vim9script 2 | 3 | import './workstation.vim' as ws 4 | 5 | 6 | def Desaturate() 7 | ws.AddMatchHighlight('StargateDesaturate', 1001) 8 | enddef 9 | 10 | 11 | def Designations(length: number): list 12 | final ds = ws.LabelLists(g:stargate_chars, length) 13 | 14 | # remove unwanted labels from start and end of the label list 15 | var slice = ds.labels[ds.start_row : ds.end_row] 16 | const start = slice->remove(0)[ds.start_col :] 17 | const end = slice->insert(start)->remove(-1)[: ds.end_col] 18 | slice->add(end) 19 | 20 | # shuffle designations 21 | var dss = [] 22 | for i in range(ds.len) 23 | for j in range(len(slice)) 24 | const label = slice[j]->get(i, '') 25 | if !empty(label) 26 | dss->add(label) 27 | endif 28 | endfor 29 | endfor 30 | 31 | return dss 32 | enddef 33 | 34 | 35 | def OrbitalStars(pattern: string, flags: string, orbit: number): list> 36 | cursor(orbit, 1) 37 | var stars: list> 38 | var star = searchpos(pattern, flags, orbit) 39 | while star[0] != 0 40 | stars->add(star) 41 | const first = $'\%>{star[1]}c' 42 | star = searchpos(first .. pattern, flags, orbit) 43 | endwhile 44 | return stars 45 | enddef 46 | 47 | 48 | # Returns list of list of collected stars and error if any 49 | def CollectStars(orbits: list, cur_loc: list, pat: string): list> 50 | var stars = [] 51 | for orbit in orbits 52 | if strdisplaywidth(getline(orbit)) > ws.max_col 53 | throw $'stargate: detected a line that is longer than {ws.max_col}' .. 54 | ' characters. It can be slow, so plugin disabled.' 55 | endif 56 | 57 | var orbital_stars = OrbitalStars(pat, 'Wnc', orbit) 58 | if orbit == cur_loc[0] 59 | for i in range(len(orbital_stars)) 60 | if orbital_stars[i][1] == cur_loc[1] 61 | orbital_stars->remove(i) 62 | break 63 | endif 64 | endfor 65 | endif 66 | stars->add(orbital_stars) 67 | endfor 68 | return stars->flattennew(1) 69 | enddef 70 | 71 | 72 | def GalaxyStars(pattern: string): list> 73 | const arc = ws.OrbitalArc() 74 | var degrees = {first: '', last: ''} 75 | if !&wrap 76 | degrees.first = $'\%>{arc.first - 1}v' 77 | degrees.last = $'\%<{arc.last + 1}v' 78 | endif 79 | 80 | const pat = degrees.first .. degrees.last .. pattern 81 | const cur_loc = [ 82 | ws.winview.lnum, 83 | ws.winview.col + 1 84 | ] 85 | const stars = ws.OrbitsWithoutBlackmatter(ws.win.topline, ws.win.botline) 86 | ->CollectStars(cur_loc, pat) 87 | 88 | winrestview(ws.winview) 89 | return stars 90 | enddef 91 | 92 | 93 | def ChooseColor(prev: dict, orbit: number, degree: number): string 94 | if orbit == prev.orbit 95 | && prev.len >= degree - prev.degree 96 | && prev.color == 'StargateMain' 97 | return 'StargateSecondary' 98 | endif 99 | return 'StargateMain' 100 | enddef 101 | 102 | 103 | def ShowStargates(destinations: list>): dict 104 | const length = len(destinations) 105 | const names = Designations(length) 106 | var prev = { orbit: -1, degree: -1, len: 0, color: 'StargateMain' } 107 | var stargates: dict 108 | 109 | # Check if some outside force closed some of stargate popups 110 | # mostly for popup_clear(), will fail on some manual popup_remove(id) 111 | if empty(popup_getpos(ws.label_windows[g:stargate_chars[0]])) 112 | for id in values(ws.label_windows) 113 | popup_close(id) 114 | endfor 115 | ws.CreateLabelWindows() 116 | endif 117 | 118 | const galaxy_distant_orbit = win_screenpos(0)[0] - 1 + winheight(0) 119 | for i in range(length) 120 | const orbit = destinations[i][0] 121 | const degree = destinations[i][1] 122 | const scr_pos = screenpos(0, orbit, degree) 123 | if scr_pos.row > galaxy_distant_orbit 124 | break 125 | endif 126 | const name = names[i] 127 | const id = ws.label_windows[name] 128 | const color = ChooseColor(prev, orbit, degree) 129 | const zindex = 100 + i 130 | popup_move(id, { line: scr_pos.row, col: scr_pos.curscol }) 131 | popup_setoptions(id, { highlight: color, zindex: zindex }) 132 | popup_show(id) 133 | stargates[name] = { id: id, orbit: orbit, degree: degree, color: color, zindex: zindex } 134 | prev = { orbit: orbit, degree: degree, len: len(name), color: color } 135 | endfor 136 | 137 | return stargates 138 | enddef 139 | 140 | 141 | export def GetDestinations(pattern: string, is_regex: bool): dict 142 | var destinations = GalaxyStars(ws.TransformPattern(pattern, is_regex)) 143 | const length = len(destinations) 144 | 145 | var stargates: dict 146 | if length == 0 147 | stargates = {} 148 | elseif length == 1 149 | stargates = {jump: {orbit: destinations[0][0], degree: destinations[0][1]}} 150 | elseif length > g:stargate_limit 151 | throw $'stargate: too much popups to show - {length}' 152 | else 153 | Desaturate() 154 | stargates = destinations->ShowStargates() 155 | endif 156 | 157 | return stargates 158 | enddef 159 | 160 | # vim: sw=4 161 | -------------------------------------------------------------------------------- /import/stargate/vim9000.vim: -------------------------------------------------------------------------------- 1 | vim9script 2 | 3 | import './stargates.vim' as sg 4 | import './galaxies.vim' 5 | import './messages.vim' as msg 6 | import './workstation.vim' as ws 7 | 8 | var in_visual_mode: bool 9 | var is_hlsearch: bool 10 | var stargate_visual: list> 11 | var stargate_showmode: bool 12 | const match_paren_enabled = exists(':DoMatchParen') == 2 ? true : false 13 | 14 | 15 | # `mode` can be positive number or string 16 | # when it is number search for that much consequitive input chars 17 | # when `mode` is string it is regex to search for 18 | export def OkVIM(mode: any) 19 | try 20 | Greetings() 21 | var destinations: dict 22 | if type(mode) == v:t_number 23 | destinations = ChooseDestinations(mode) 24 | else 25 | destinations = sg.GetDestinations(mode, true) 26 | endif 27 | if !empty(destinations) 28 | normal! m' 29 | if len(destinations) == 1 30 | msg.BlankMessage() 31 | cursor(destinations.jump.orbit, destinations.jump.degree) 32 | else 33 | UseStargate(destinations) 34 | endif 35 | endif 36 | catch 37 | winrestview(ws.winview) 38 | if v:exception =~ '^\s*stargate:' 39 | msg.Warning(v:exception) 40 | else 41 | redraw 42 | exe $'echoerr "{v:exception}"' 43 | endif 44 | finally 45 | Goodbye() 46 | endtry 47 | enddef 48 | 49 | 50 | def HideLabels(stargates: dict) 51 | for v in values(stargates) 52 | popup_hide(v.id) 53 | endfor 54 | enddef 55 | 56 | 57 | def Saturate() 58 | ws.RemoveMatchHighlight(ws.win['StargateError']) 59 | ws.RemoveMatchHighlight(ws.win['StargateDesaturate']) 60 | enddef 61 | 62 | 63 | def HideStarsHints() 64 | for v in values(ws.label_windows) 65 | popup_hide(v) 66 | endfor 67 | enddef 68 | 69 | 70 | def Greetings() 71 | ws.winview = winsaveview() 72 | 73 | in_visual_mode = mode() != 'n' 74 | if in_visual_mode 75 | stargate_showmode = &showmode 76 | &showmode = 0 77 | stargate_visual = hlget('Visual') 78 | hlset([{name: 'Visual', cleared: true, linksto: 'StargateVisual'}]) 79 | endif 80 | 81 | ws.UpdateWinBounds() 82 | 83 | is_hlsearch = v:hlsearch 84 | if is_hlsearch 85 | &hlsearch = 0 86 | endif 87 | 88 | if match_paren_enabled 89 | ws.RemoveMatchHighlight(3) 90 | endif 91 | 92 | ws.SetScreen() 93 | msg.StandardMessage($'{g:stargate_name}, choose a destination.') 94 | enddef 95 | 96 | 97 | def Goodbye() 98 | HideStarsHints() 99 | Saturate() 100 | ws.ClearScreen() 101 | 102 | # rehighlight matched paren 103 | doautocmd CursorMoved 104 | 105 | if is_hlsearch 106 | &hlsearch = 1 107 | endif 108 | 109 | if in_visual_mode 110 | &showmode = stargate_showmode 111 | hlset(stargate_visual) 112 | endif 113 | enddef 114 | 115 | 116 | def ShowFiltered(stargates: dict) 117 | for [label, stargate] in items(stargates) 118 | const id = ws.label_windows[label] 119 | const scr_pos = screenpos(0, stargate.orbit, stargate.degree) 120 | popup_move(id, { line: scr_pos.row, col: scr_pos.col }) 121 | popup_setoptions(id, { highlight: stargate.color, zindex: stargate.zindex }) 122 | popup_show(id) 123 | stargates[label].id = id 124 | endfor 125 | enddef 126 | 127 | 128 | def UseStargate(destinations: dict) 129 | var stargates = copy(destinations) 130 | msg.StandardMessage('Select a stargate for a jump.') 131 | while true 132 | var filtered = {} 133 | const [nr: number, err: bool] = ws.SafeGetChar() 134 | 135 | if err || nr == 27 # 27 is 136 | msg.BlankMessage() 137 | return 138 | endif 139 | 140 | const char = nr2char(nr) 141 | for [label, stargate] in items(stargates) 142 | if match(label, char) == 0 143 | const new_label = strcharpart(label, 1) 144 | filtered[new_label] = stargate 145 | endif 146 | endfor 147 | 148 | if empty(filtered) 149 | msg.Error($'Wrong stargate, {g:stargate_name}. Choose another one.') 150 | elseif len(filtered) == 1 151 | msg.BlankMessage() 152 | cursor(filtered[''].orbit, filtered[''].degree) 153 | return 154 | else 155 | HideLabels(stargates) 156 | ShowFiltered(filtered) 157 | stargates = copy(filtered) 158 | msg.StandardMessage('Select a stargate for a jump.') 159 | endif 160 | endwhile 161 | enddef 162 | 163 | 164 | def ChooseDestinations(mode: number): dict 165 | var to_galaxy = false 166 | var destinations = {} 167 | var pattern: string 168 | while true 169 | var nrs = [] 170 | for _ in range(mode) 171 | const [nr: number, err: bool] = ws.SafeGetChar() 172 | 173 | if err || nr == 27 # 27 is 174 | msg.BlankMessage() 175 | return {} 176 | endif 177 | 178 | if nr == 23 # 23 is 179 | to_galaxy = true 180 | break 181 | endif 182 | 183 | nrs->add(nr) 184 | endfor 185 | 186 | if to_galaxy 187 | to_galaxy = false 188 | if in_visual_mode || ws.InOperatorPendingMode() 189 | msg.Error($'It is impossible to do now, {g:stargate_name}.') 190 | elseif !galaxies.ChangeGalaxy(false) 191 | return {} 192 | endif 193 | ws.winview = winsaveview() 194 | 195 | # if current window after the jump is in terminal or insert modes - quit stargate 196 | if match(mode(), '[ti]') == 0 197 | throw "stargate: can't work in terminal or insert mode." 198 | endif 199 | continue 200 | endif 201 | 202 | pattern = nrs 203 | ->mapnew((_, v) => nr2char(v)) 204 | ->join('') 205 | destinations = sg.GetDestinations(pattern, false) 206 | if empty(destinations) 207 | msg.Error($"We can't reach there, {g:stargate_name}.") 208 | continue 209 | endif 210 | break 211 | endwhile 212 | 213 | return destinations 214 | enddef 215 | 216 | # vim: sw=4 217 | -------------------------------------------------------------------------------- /import/stargate/workstation.vim: -------------------------------------------------------------------------------- 1 | vim9script 2 | 3 | export const max_col = 5000 4 | export var label_windows: dict 5 | export var winview: dict 6 | export var win: dict = { 7 | topline: 0, 8 | botline: 0, 9 | lines_range: [], 10 | StargateFocus: 0, 11 | StargateDesaturate: 0, 12 | StargateError: 0, 13 | } 14 | var conceal_level: number 15 | var fake_cursor_match_id: number 16 | 17 | 18 | # Creates plugin highlights 19 | export def CreateHighlights() 20 | highlight default StargateFocus guifg=#958c6a 21 | highlight default StargateDesaturate guifg=#49423f 22 | highlight default StargateError guifg=#d35b4b 23 | highlight default StargateLabels guifg=#caa247 guibg=#171e2c 24 | highlight default StargateErrorLabels guifg=#caa247 guibg=#551414 25 | highlight default StargateMain guifg=#f2119c gui=bold cterm=bold 26 | highlight default StargateSecondary guifg=#11eb9c gui=bold cterm=bold 27 | highlight default StargateShip guifg=#111111 guibg=#caa247 28 | highlight default StargateVIM9000 guifg=#111111 guibg=#b2809f gui=bold cterm=bold 29 | highlight default StargateMessage guifg=#a5b844 30 | highlight default StargateErrorMessage guifg=#e36659 31 | highlight default link StargateVisual Visual 32 | enddef 33 | 34 | 35 | # Returns first window column number after signcolumn 36 | export def DisplayLeftEdge(): number 37 | return win_getid() 38 | ->getwininfo()[0].textoff + 1 39 | enddef 40 | 41 | 42 | # Returns `true` if 'list' option is set and 'listchars' has 'precedes' 43 | def ListcharsHasPrecedes(): bool 44 | return &list && match(&listchars, 'precedes') != -1 45 | enddef 46 | 47 | 48 | # Creates new matchadd highlight and additionally removes any leftover 49 | # highlights from the previous highlighting of this `match_group` 50 | # Useful when adding match highlight with the `timer_start()` 51 | export def AddMatchHighlight(match_group: string, priority: number) 52 | const id = matchaddpos(match_group, win.lines_range, priority) 53 | RemoveMatchHighlight(win[match_group]) 54 | win[match_group] = id 55 | enddef 56 | 57 | # Silently removes match highlight with `match_id` 58 | export def RemoveMatchHighlight(match_id: number) 59 | silent! call matchdelete(match_id) 60 | enddef 61 | 62 | # Returns first and last visible virtual columns of the buffer in the current window 63 | export def OrbitalArc(): dict 64 | const edge = DisplayLeftEdge() 65 | var last_degree = 0 66 | var first_degree = virtcol('.') - wincol() + edge 67 | if first_degree > 1 && ListcharsHasPrecedes() 68 | first_degree += 1 69 | last_degree -= 1 70 | endif 71 | last_degree += first_degree + winwidth(0) - edge 72 | return { first: first_degree, last: last_degree } 73 | enddef 74 | 75 | 76 | # Sets some new values for global `win` dictionary 77 | export def UpdateWinBounds() 78 | win.topline = line('w0') 79 | win.botline = line('w$') 80 | win.lines_range = range(win.topline, win.botline) 81 | enddef 82 | 83 | 84 | # Returns list of all visible lines of the current window buffer from top to bottom. 85 | # Excluding folded lines 86 | export def OrbitsWithoutBlackmatter(near: number, distant: number): list 87 | var current = near 88 | var orbits = [] 89 | while current <= distant 90 | const last_bm_orbit = foldclosedend(current) 91 | # foldclosedend() returns -1 if not in closed fold 92 | if last_bm_orbit != -1 93 | current = last_bm_orbit + 1 94 | else 95 | orbits->add(current) 96 | current += 1 97 | endif 98 | endwhile 99 | 100 | return orbits 101 | enddef 102 | 103 | 104 | # Returns new pattern with all alternative branches for pattern 105 | # found in g:stargate_keymaps or unmodified 106 | def ProcessKeymap(pattern: string): string 107 | var pat: string 108 | for char in (split(pattern, '\zs')) 109 | const rhs = get(g:stargate_keymaps, char, '') 110 | if empty(rhs) 111 | pat ..= char 112 | else 113 | pat ..= $'\[{char}{rhs}]' 114 | endif 115 | endfor 116 | 117 | return pat 118 | enddef 119 | 120 | 121 | # Retruns true in operator-pending mode 122 | export def InOperatorPendingMode(): bool 123 | return state()[0] == 'o' 124 | enddef 125 | 126 | 127 | # Returns modified pattern so it can be processed by searchpos() 128 | export def TransformPattern(pattern: string, is_regex: bool): string 129 | if is_regex 130 | return pattern 131 | elseif pattern == ' ' 132 | return '\S\zs\s' 133 | endif 134 | 135 | var pat = pattern 136 | const prefix = '\V' .. (g:stargate_ignorecase ? '\c' : '\C') 137 | if !empty(g:stargate_keymaps) 138 | pat = ProcessKeymap(pat) 139 | endif 140 | 141 | return prefix .. pat 142 | enddef 143 | 144 | 145 | # GetLabels(['a', 'b'], 5) -> 146 | # {labels: [['a', 'b'], ['aa', 'ab'], ['ba', 'bb'], ['aaa', 'aab']], 147 | # start_row: 1, start_col: 1, end_row: 3, end_col: 1, len: 2} 148 | export def LabelLists(chars: list, length: number): dict 149 | const chars_len = len(chars) 150 | const one_less = chars_len - 1 151 | var total_len = length 152 | var labels = [copy(chars)] 153 | 154 | var index = 0 155 | var i1 = index / chars_len 156 | var i2 = index % chars_len 157 | while total_len > chars_len 158 | const char = labels[i1][i2] 159 | labels->add(MapnewConcat(chars, char)) 160 | total_len -= one_less 161 | index += 1 162 | i1 = index / chars_len 163 | i2 = index % chars_len 164 | endwhile 165 | 166 | const labels_len = len(labels) 167 | const end_row = labels_len - 1 168 | const end_col = chars_len - (labels_len * chars_len - length - index + 1) 169 | 170 | return { 171 | labels: labels, 172 | len: chars_len, 173 | start_row: i1, 174 | start_col: i2, 175 | end_row: end_row, 176 | end_col: end_col 177 | } 178 | enddef 179 | 180 | 181 | # Returns new list with each string element in it prefixed with `char` 182 | # MapnewConcat(['x', 'y', 'z'], 'a') -> ['ax', 'ay', 'az'] 183 | def MapnewConcat(strings: list, char: string): list 184 | var result = [] 185 | for str in strings 186 | result->add(char .. str) 187 | endfor 188 | return result 189 | enddef 190 | 191 | 192 | # Returns result and error state, but do not break out 193 | # from invoking of getchar() immediately. Replaces all NaN results with -1 194 | export def SafeGetChar(): list 195 | var nr: number 196 | var err = false 197 | try 198 | nr = getchar() 199 | if type(nr) != v:t_number 200 | nr = -1 201 | endif 202 | catch 203 | err = true 204 | endtry 205 | return [nr, err] 206 | enddef 207 | 208 | 209 | export def CreateLabelWindows() 210 | label_windows = {} 211 | const labels = LabelLists(g:stargate_chars, g:stargate_limit).labels->flattennew(1) 212 | for ds in labels 213 | label_windows[ds] = popup_create(ds, { line: 0, col: 0, hidden: true, wrap: false, tabpage: -1 }) 214 | endfor 215 | enddef 216 | 217 | 218 | export var HideCursor: func() 219 | export var ShowCursor: func() 220 | # Hiding the cursor when awaiting for char of getchar() function 221 | # done differently in gui and terminal 222 | if has('gui_running') 223 | var cursor_state: list> 224 | HideCursor = () => { 225 | cursor_state = hlget('Cursor') 226 | hlset([{name: 'Cursor', cleared: true}]) 227 | } 228 | ShowCursor = () => { 229 | hlset(cursor_state) 230 | } 231 | else 232 | var cursor_state: string 233 | HideCursor = () => { 234 | cursor_state = &t_ve 235 | &t_ve = '' 236 | } 237 | ShowCursor = () => { 238 | &t_ve = cursor_state 239 | } 240 | endif 241 | 242 | 243 | export def SetScreen() 244 | conceal_level = &conceallevel 245 | &conceallevel = 0 246 | HideCursor() 247 | 248 | fake_cursor_match_id = matchaddpos('StargateShip', [[line('.'), col('.')]], 1010) 249 | AddMatchHighlight('StargateFocus', 1000) 250 | enddef 251 | 252 | 253 | export def ClearScreen() 254 | RemoveMatchHighlight(win['StargateFocus']) 255 | RemoveMatchHighlight(fake_cursor_match_id) 256 | ShowCursor() 257 | &conceallevel = conceal_level 258 | enddef 259 | 260 | # vim: sw=4 261 | -------------------------------------------------------------------------------- /test/workstation.vim: -------------------------------------------------------------------------------- 1 | import 'stargate/workstation.vim' as ws 2 | 3 | let s:assert = themis#helper('assert') 4 | 5 | let workstation = themis#suite('import/workstation.vim') 6 | 7 | 8 | func workstation.before_each() 9 | new 10 | endfunc 11 | 12 | func workstation.after_each() 13 | close! 14 | endfunc 15 | 16 | func workstation.DisplayLeftEdge() 17 | let ws.winview = winsaveview() 18 | 19 | call s:assert.equals(s:ws.DisplayLeftEdge(), 1, 20 | \ 'without sign and linenr columns') 21 | 22 | set signcolumn=yes 23 | call s:assert.equals(s:ws.DisplayLeftEdge(), 3, 24 | \ 'with sign column') 25 | 26 | set nu rnu 27 | call s:assert.equals(s:ws.DisplayLeftEdge(), 7, 28 | \ 'with sign and linnr columns') 29 | 30 | set signcolumn=no 31 | call s:assert.equals(s:ws.DisplayLeftEdge(), 5, 32 | \ 'with linnr column') 33 | endfunc 34 | 35 | 36 | func workstation.UpdateWinBounds() 37 | call setline(1, repeat(['line'], 20)) 38 | normal 10ggzt 39 | call s:ws.UpdateWinBounds() 40 | call s:assert.equals(s:ws.win.topline, line('w0'), 41 | \ 'first visible line number ') 42 | call setline(1, repeat(['line'], 20)) 43 | normal 10ggzt 44 | call s:assert.equals(s:ws.win.botline, line('w$'), 45 | \ 'last visible line number ') 46 | endfunc 47 | 48 | 49 | func workstation.OrbitalArc() 50 | let win_width = winwidth(0) 51 | 52 | call s:assert.equals(s:ws.OrbitalArc().first, 1, 53 | \ 'first virtual column for empty buffer') 54 | call s:assert.equals(s:ws.OrbitalArc().last, winwidth(0), 55 | \ 'last visible virtual column for empty buffer') 56 | 57 | call setline(1, repeat('word ', 200)) 58 | exe 'normal ' .. win_width .. 'l' 59 | let ws.winview = winsaveview() 60 | call s:assert.equals(s:ws.OrbitalArc().first, win_width + 1, 61 | \ 'first virtual column for shifted text') 62 | call s:assert.equals(s:ws.OrbitalArc().last, 2 * win_width, 63 | \ 'last virtual column for shifted text') 64 | 65 | set list 66 | set listchars+=precedes:0 67 | call s:assert.equals(s:ws.OrbitalArc().first, win_width + 2, 68 | \ 'first virtual column for shifted text with `precedes`') 69 | call s:assert.equals(s:ws.OrbitalArc().last, 2 * win_width, 70 | \ 'last virtual column for shifted text with `precedes`') 71 | endfunc 72 | --------------------------------------------------------------------------------