Compare commits

...

170 Commits

Author SHA1 Message Date
Zachary Yedidia
5c8a2332d9 Use unicode.Mark for combining unicode range 2020-05-20 18:01:10 -04:00
Zachary Yedidia
ff0683d6d0 Final touches for combining character support 2020-05-20 17:00:56 -04:00
Zachary Yedidia
79c0ea17ad Use CharacterCount over RuneCount 2020-05-20 16:47:08 -04:00
Zachary Yedidia
bdff221870 Use DecodeCharacter over DecodeRune 2020-05-20 16:43:12 -04:00
Zachary Yedidia
65be5efd83 Merge branch 'p-e-w-buffer-benchmarks' 2020-05-20 15:29:02 -04:00
Zachary Yedidia
a491dd1c52 Merge branch 'buffer-benchmarks' of https://github.com/p-e-w/micro into p-e-w-buffer-benchmarks 2020-05-20 15:28:36 -04:00
Zachary Yedidia
d7ab44253f Update tcell and support italics in colorschemes
Closes #1640
2020-05-17 12:48:34 -04:00
Zachary Yedidia
0a6720498f Merge branch 'master' of https://github.com/zyedidia/micro 2020-05-17 12:23:21 -04:00
dmaluka
a150eef6f9 Fix end line number in HighlightMatches (#1662)
There is a bit of mess in the usage of HighlightMatches: in some places
we assume that it updates lines from startline to endline inclusive,
in other places we assume it's non-inclusive.
This fix makes it always inclusive.

In particular, it fixes a bug: when we open a file which has no
newline at the end, the last line isn't highlighted.
2020-05-17 16:05:34 -04:00
Zachary Yedidia
c46257222c Add support for FindLiteral
Use the FindLiteral action to use Find without regex support.

Fixes #1661
2020-05-17 12:22:33 -04:00
jsyedidia
299af4a3db Update hlint to 3.0 syntax (#1659) 2020-05-16 13:06:55 -04:00
Jeff Warner
d0f7ecf9ca Adds command "tabmove ±n", for better tab management (#1636)
* Adds command "tabmove ±n", for better tab management

* Added tabmove to help:commands

* Replace uses of util.Min, util.Max with util.Clamp

Browsing code and discovered `util.Clamp`, ideal for this section of my code

* oops, missed an arg

* Typo, again
2020-05-14 21:51:49 -04:00
dmaluka
fb35e0312a Fix unbind of a rune (#1649)
Fix problem with non-working unbind of a rune key.
E.g. after the following commands:

bind "n" "FindNext"
unbind "n"

Observed result: "n" key still triggers FindNext action
Expected result: "n" key inserts "n" rune
2020-05-14 21:50:28 -04:00
Zachary Yedidia
30395b1f67 Remove outdated c++ highlighter
Fixes #1652
2020-05-14 21:37:19 -04:00
Zachary Yedidia
ddf70953fe Support snake case autocompletion
Fixes #1655
2020-05-14 21:34:17 -04:00
Zachary Yedidia
55e97596d3 Fix movelinesup when selection is not complete 2020-05-07 19:39:17 -04:00
Zachary Yedidia
66dc48ce9b Improve readme 2020-05-04 22:21:46 -04:00
Zachary Yedidia
c490a94700 Update makefile 2020-05-04 10:34:16 -04:00
Zachary Yedidia
eff89a98a7 Fix v2 import path for go mod 2020-05-04 10:16:15 -04:00
Zachary Yedidia
221d8f462a Merge branch 'jwarner112-jwarner112-copyline' 2020-04-30 00:54:11 -04:00
Zachary Yedidia
7a23878250 gofmt 2020-04-30 00:54:02 -04:00
Jeff Warner
5d3e4fc3d9 Adds CopyLine action, the new default action for CtrlC if cursor has no selection 2020-04-29 21:06:54 -07:00
Indiana Kernick
f52fbfa1f0 Add .inl as a C++ file extension (#1630) 2020-04-29 20:01:59 -04:00
Zachary Yedidia
d60626c64b Merge 2020-04-25 17:01:20 -04:00
Zachary Yedidia
aaac0b1e6f Better actions error message 2020-04-25 17:01:16 -04:00
Some person
0f984131fb Update coffeescript.yaml (#1571)
* Update coffeescript.yaml

We need much much more modern coffeescript standards, the current one has broken `0x123456` (hex) and single quotes, and doesn't support multiline comments. This PR aims to fix that. I'm no regexp expert, I just based this off JS', so tell me if I did anything wrong.

* Update coffeescript.yaml
2020-04-22 23:12:56 -04:00
Zachary Yedidia
eb7189dcdb Make cursor follow selections
Fixes #1624
2020-04-21 09:33:21 -04:00
Zachary Yedidia
74523d28c5 Merge 2020-04-20 23:13:10 -04:00
Zachary Yedidia
f894f0a26e Update clipboard version 2020-04-20 23:13:01 -04:00
2pac
a067ce1f41 implemented circular tab movement (#1619)
Co-authored-by: 2pac <tarasyarema@pm.me>
2020-04-17 13:42:48 -04:00
Zachary Yedidia
f59468642d Update runtime 2020-04-10 17:27:57 -04:00
Zachary Yedidia
85e85b7ccc Merge 2020-04-10 17:27:34 -04:00
Zachary Yedidia
8f5888e7bf Use StartCol in colorcolumn calculation
Fixes #1615
2020-04-10 17:27:11 -04:00
Ján Jančár
f0da73bae2 Add StartOfTextToggle and SelectToStartOfTextToggle actions. (#1612)
These actions reintroduce the behavior of micro where the Home key
toggles between the start of text (first) and the start of the line.
The same applies for the variant with selection. This commit also
sets these bindings as the defaults.
2020-04-10 17:21:02 -04:00
Zachary Yedidia
d92deacf99 Ensure mouse release before focus change
Fixes #1613
2020-04-10 15:58:43 -04:00
trrbl
ffd7b5c770 Support csharp-script syntax. (#1425)
```
#!/usr/bin/env dotnet-script
// Set Runtime
#! "netcoreapp3.0"
// Imports
#load "myAssembly.dll"
#r "nuget:CliWrap,2.5.0"
```
This syntax file basically imports the `csharp` rules and adds it's custom pre-processors.
2020-04-10 13:57:36 -04:00
Ján Jančár
bd8c0d25c8 Add sagemath syntax highlight based on python3. (#1227) 2020-04-09 12:20:48 -04:00
Ján Jančár
052a36b896 Fix docs regarding "Home" key and "StartOfText" and "StartOfLine". (#1611) 2020-04-07 12:58:30 -04:00
jsyedidia
a76bf02f5f Add Haskell linter hlint to linter plugin (#1610) 2020-04-05 14:05:01 -04:00
Nikita Bobko
92f4cb7ef7 Clarify regex for git commit --verbose (#1606) 2020-04-05 14:04:36 -04:00
Zachary Yedidia
1cf9537340 Fix python3 syntax file and make python3 default
The python3 syntax had "filename" instead of "filetype"
as the header. This commit also makes standard py extensions
use the python3 highlighting and requires .py2 or a python2
env to use python2 highlighting because python3 is the standard
python now.

Fixes #1592
2020-03-24 11:42:23 -04:00
Zachary Yedidia
60c8c81da3 Relocate during replace
Fixes #1587
2020-03-24 11:33:52 -04:00
Zachary Yedidia
c76a973877 Merge 2020-03-24 11:17:12 -04:00
Zachary Yedidia
6def99ce24 Clarify replace message if replacing in selection 2020-03-24 11:14:54 -04:00
Koki Fushimi
26930ca81f Better Julia syntax. (#1567)
* Fix regex syntax and change to match one or more spaces.

* Add constant `nothing` and `missing`.

* Add Inf and NaN to constant numbers.
2020-03-24 10:59:48 -04:00
Hugo Locurcio
cd379cd838 Clarify the Find operation being regex-enabled (#1561)
This makes it more obvious that the Find option accepts regular
expressions as input.

See discussion in #1560.
2020-03-24 10:59:40 -04:00
allanderek
ee157f6503 Add elm as a default comment type in the comment plugin. (#1586) 2020-03-24 10:56:50 -04:00
Zachary Yedidia
48ca19873f Better ordering for reading syntax files
Ref #1580
2020-03-24 10:52:15 -04:00
Zachary Yedidia
fee5528309 Fix term emulator crash if invalid exec given
Ref #1583
2020-03-24 10:22:10 -04:00
Zachary Yedidia
671a188802 Support +LINE:COL flag syntax for cursor pos
Closes #1566
2020-03-24 10:10:44 -04:00
Zachary Yedidia
18d540583b Don't clear infobar if not enabled
Fixes #1584
2020-03-17 14:21:36 -04:00
Zachary Yedidia
943ea15fa3 Fix linter c++ entry
Fixes #1578
2020-03-14 15:40:05 -04:00
Cafe Duke
2ef57977d7 Add color schemes dukeubuntu-tc, dukedark-tc and dukelight-tc (#1547)
* Duke ubuntu, dark and light color schemes

* Duke color schemes: Change bgcolor for line number and cursor line

Co-authored-by: Raghunandan.Seshadri <raghubs81@gmail.com>
Co-authored-by: Raghunandan Seshadri <raghunandan.seshadri@oracle.com>
2020-03-07 22:07:43 -05:00
Zachary Yedidia
527750b68d Copy selection to primary on mouse release
Fixes #1558
2020-03-05 16:00:40 -05:00
Zachary Yedidia
629efe5eb7 Add JumpLine action back
You can bind to "command-edit:goto ", but binding to the action
"JumpLine" will have the same effect now.

Fixes #1550
2020-03-02 20:09:19 -05:00
Andrew Havens
a19cd2e6d0 Add support for Fastlane and Cocoapods file syntax highlighting (#1544) 2020-03-02 20:03:28 -05:00
Philipp Emanuel Weidmann
d038d3040f Add more sophisticated buffer benchmark system 2020-03-01 13:20:10 +05:30
Zachary Yedidia
9e8d76f2fa If stdout is a pipe, output to the pipe
If you run micro as `micro | cat` for example, micro will disallow
you from saving the file, and when you quit the buffer, the contents
will be sent to the pipe. This allows one to use micro as part of
an interactive unix pipeline.

Closes #1524
2020-02-27 12:39:19 -05:00
Zachary Yedidia
8a9a14562f Use bytes.Buffer for LineArray.Bytes 2020-02-27 11:27:00 -05:00
Zachary Yedidia
a6f5dee45c Fix custom syntax files not highlighting
Fixes #1530
2020-02-27 00:58:52 -05:00
Zachary Yedidia
b12886b066 Improve buffer test 2020-02-25 23:59:27 -05:00
Zachary Yedidia
56f5b475eb Improve buffer test 2020-02-25 23:21:50 -05:00
Zachary Yedidia
c51f84955e Update runtime 2020-02-25 21:08:22 -05:00
Zachary Yedidia
e4bf1e9984 Undo event chunks instead of single events 2020-02-25 20:53:48 -05:00
Zachary Yedidia
917e2922a1 Update readme 2020-02-25 20:40:57 -05:00
Zachary Yedidia
59e8f3aa3e mod tidy 2020-02-25 20:28:02 -05:00
Zachary Yedidia
53bda0cfa7 Fix buffer tests and selection bug
Fixes #1528
Ref #1526
2020-02-25 20:24:02 -05:00
Zachary Yedidia
f059541e0d Merge branch 'buffer-tests' of https://github.com/p-e-w/micro into buffer-unit-tests 2020-02-25 10:30:31 -05:00
Zachary Yedidia
d78fe81e21 line_array insert for eofnewline and make default
Makes the `eofnewline` option enabled by default.

Fixes #1525
2020-02-24 22:31:05 -05:00
josh
25b9342fbe fix eofnewline not running on files with 1 rune (#1535) 2020-02-24 22:26:51 -05:00
Zachary Yedidia
70bcf9f618 Fix text transformation bug
This fixes the remaining text transformation tests.

Ref #1526
2020-02-24 20:11:11 -05:00
Roman Kornev
8848388411 Hide ISSUE_TEMPLATE version help into a comment (#1532)
Because some people don't remove it
2020-02-24 13:49:45 -05:00
Zachary Yedidia
dff8b33e9c Apply basename option in tabbar as well 2020-02-24 13:48:37 -05:00
Zachary Yedidia
8a2048e7f6 Use tabbar color group, and mark modified tabs
Fixes #1523
2020-02-24 13:45:10 -05:00
Zachary Yedidia
0174d7dba4 Move multi-cursors correctly after newlines
Fixes #1527
2020-02-24 13:39:34 -05:00
Zachary Yedidia
e1827480c9 Filename completion for all non-command prompts
Fixes #1529
2020-02-24 13:00:55 -05:00
Zachary Yedidia
d8584d1ddb Debug off using default "go build"
Ref #1469
2020-02-24 12:55:59 -05:00
Philipp Emanuel Weidmann
f0cdc3cabb Add buffer test and benchmark suite (and tool to generate it) 2020-02-22 08:51:38 +05:30
Zachary Yedidia
2ef4f83358 Fix issue with simultaneous buffers 2020-02-19 17:40:54 -05:00
Zachary Yedidia
a9120ce270 Share more buffer elements and fix rehighlight
Fixes #1521
2020-02-19 14:41:30 -05:00
Zachary Yedidia
190d9d0609 Tweak version build script 2020-02-19 05:27:33 +00:00
Zachary Yedidia
cf3fdb344a Merge 2020-02-18 21:40:36 -05:00
Zachary Yedidia
b91242124c Go lint the current directory of file
Closes #1520
2020-02-18 21:40:14 -05:00
Zachary Yedidia
5ffc19f159 Use filecomplete for shell mode 2020-02-17 22:29:33 -05:00
Zachary Yedidia
cc994b6241 Fix relocation with softwrap on small buffers
Fixes #1512
2020-02-15 15:38:20 -05:00
Zachary Yedidia
087e7207f7 Add 'xterm' option
Ref #1489
2020-02-15 12:53:17 -05:00
Zachary Yedidia
db32b84cd1 Relocate after rune insert
Fixes #1510
2020-02-14 15:52:20 -05:00
Zachary Yedidia
00006aa2b4 Update snap version info 2020-02-13 21:27:07 -05:00
Zachary Yedidia
600d8558b2 Change some default option values 2020-02-13 20:51:56 -05:00
Zachary Yedidia
4874823240 Fix makefile tags dependencies 2020-02-13 20:00:35 -05:00
Zachary Yedidia
6a0e4b5564 Fetch tags before snapcraft build 2020-02-13 19:57:31 -05:00
Zachary Yedidia
b2d7c8c5d4 Merge 2020-02-13 19:49:27 -05:00
Zachary Yedidia
38f88ade60 Search and replace within a selection
Closes #1098
2020-02-13 19:48:48 -05:00
Tonus1
8348cc8ec2 Add transparency (#1509) 2020-02-13 18:15:32 -05:00
Zachary Yedidia
743d42e417 Syntax file change 2020-02-13 16:50:44 -05:00
Zachary Yedidia
faa207907c Handle terminal paste and raw events in info bar 2020-02-13 16:10:35 -05:00
Zachary Yedidia
9d1c489574 Merge 2020-02-13 16:09:16 -05:00
Zachary Yedidia
30ed25859a Support regex capture groups in replace command
See https://golang.org/pkg/regexp/syntax/ for the
supported syntax. Here are some examples:

```
replace "(foo)" "$1-bar"
replace "(foo)" "${1}-bar"
replace "(?P<group>foo)" "$group-bar"
replace "(?P<group>foo)" "$group-bar"
replace "(?P<key>\w+):\s+(?P<value>\w+)$" "$key=$value"
```

Closes #1115
2020-02-13 16:05:56 -05:00
Simon Ser
d2288c5f66 readme: document clipboard support on Wayland (#1508) 2020-02-13 14:00:18 -05:00
Zachary Yedidia
a07ee26b05 Fix gutter offset when softwrap is enabled 2020-02-13 11:04:10 -05:00
Zachary Yedidia
a7ce85d6f6 Make default plugin options more explicit
Ref #1305
2020-02-12 15:34:13 -05:00
Zachary Yedidia
7c71995aaf Merge branch 'utkarsh2102-update-readme' 2020-02-12 14:26:33 -05:00
Zachary Yedidia
357c4b0fcd Update readme 2020-02-12 14:26:17 -05:00
Zachary Yedidia
0d4f85304b Merge branch 'update-readme' of https://github.com/utkarsh2102/micro into utkarsh2102-update-readme 2020-02-12 14:22:33 -05:00
Zachary Yedidia
e18e41eb45 Merge branch 'seitokaichou-autosu' 2020-02-12 14:15:37 -05:00
Zachary Yedidia
5519f053ac Merge branch 'autosu' of https://github.com/seitokaichou/micro into seitokaichou-autosu 2020-02-12 14:15:30 -05:00
Zachary Yedidia
2f4b3b2a8c Merge branch 'jawahars16-bug-endless-reload-prompt' 2020-02-12 13:56:17 -05:00
Zachary Yedidia
ea290e4fb5 Merge branch 'bug-endless-reload-prompt' of https://github.com/jawahars16/micro into jawahars16-bug-endless-reload-prompt 2020-02-12 13:56:00 -05:00
Zachary Yedidia
8b414e6187 Update readme toc 2020-02-12 13:38:14 -05:00
Zachary Yedidia
e7ef81ed97 Share hash across equivalent buffers for fastdirty=off 2020-02-12 13:32:42 -05:00
Zachary Yedidia
12c286f9b1 Introduce IndentLine action
Closes #1476
2020-02-12 13:30:24 -05:00
Zachary Yedidia
7b5bc8fe37 Fix issue with global/local settings 2020-02-12 13:18:59 -05:00
Zachary Yedidia
bad78797bb Clicking tabbar arrow scrolls and fix multicursor
Closes #1503
2020-02-12 13:05:15 -05:00
Zachary Yedidia
bf1258578c Expose OpenLogBuf to plugins 2020-02-12 12:35:40 -05:00
Zachary Yedidia
6588f02f7b Only highlight matching brace if one is found
Fixes #1505
2020-02-12 01:32:23 -05:00
Zachary Yedidia
5e9c6375d0 Only fetch tags if no tags are found at all
Fixes #1504
2020-02-12 01:24:25 -05:00
Zachary Yedidia
7d47659481 Fix deleteLines off-by-one error
Fixes #1501
2020-02-12 01:16:11 -05:00
Zachary Yedidia
dcd4bae96f Clamp modifications
Fixes #1502
2020-02-12 00:55:52 -05:00
Zachary Yedidia
4f628bf30b Remove incorrect plugin documentation 2020-02-12 00:22:32 -05:00
Utkarsh Gupta
36e83a46e4 Update installation instruction on Debian systems 2020-02-12 07:45:07 +05:30
Zachary Yedidia
1a64ffb88b Don't expose draw channel to outside packages 2020-02-11 20:39:26 -05:00
Zachary Yedidia
7c77927913 Update tcell to v1.4.4 2020-02-11 20:34:22 -05:00
Zachary Yedidia
8224037080 Don't block when redraw channel becomes full
Fixes #1497
2020-02-11 20:03:32 -05:00
Zachary Yedidia
d7e3fc99f1 Merge 2020-02-11 19:13:41 -05:00
Zachary Yedidia
feaf3951d2 Update haskell syntax file 2020-02-11 19:13:36 -05:00
Zachary Yedidia
399c629076 Update readme 2020-02-11 16:40:15 -05:00
Zachary Yedidia
47ed7447f1 Update screenshot 2020-02-11 16:30:58 -05:00
Zachary Yedidia
62a98ea3c5 Typo 2020-02-11 14:50:15 -05:00
Zachary Yedidia
0a9194b883 Fix tag fetching in build-version.go 2020-02-11 14:46:19 -05:00
Zachary Yedidia
a938917b9b Fetch tags if none are found 2020-02-11 14:14:34 -05:00
Zachary Yedidia
695d4c2b1b Use filepath.Join more 2020-02-11 13:09:17 -05:00
Zachary Yedidia
34724b941a Recover from internal errors without crashing 2020-02-11 00:50:24 -05:00
Zachary Yedidia
8176e8c6f8 Improve one-dark colorscheme divider 2020-02-10 23:37:21 -05:00
Zachary Yedidia
0d5b1cd64d Update readme buttons 2020-02-10 22:58:25 -05:00
Zachary Yedidia
9fd5b133ea Add svg logo 2020-02-10 22:40:16 -05:00
Zachary Yedidia
432b57a070 Merge branch 'FJduFou-patch-1' 2020-02-10 22:37:03 -05:00
François-Joseph du Fou
ee55732be3 Update README.md 2020-02-10 22:36:57 -05:00
François-Joseph du Fou
90304fb472 better top
add a center logo + 3 news buttons
2020-02-10 22:36:02 -05:00
Zachary Yedidia
f6aec1af5f Update readme 2020-02-10 22:19:47 -05:00
Zachary Yedidia
b77b61d677 Merge branch 'Calinou-improve-readme' 2020-02-10 22:17:51 -05:00
Zachary Yedidia
05a0598f16 Merge branch 'improve-readme' of https://github.com/Calinou/micro into Calinou-improve-readme 2020-02-10 22:17:46 -05:00
Zachary Yedidia
daa6f122a7 Add note in readme about winpty 2020-02-10 22:10:01 -05:00
Zachary Yedidia
71f5f043fb Merge 2020-02-10 19:56:17 -05:00
Zachary Yedidia
f3eaf99665 Draw FakeCursor in infobar when on a character
Fixes #1496
2020-02-10 19:55:13 -05:00
Zachary Yedidia
c88c1b84da Term should return error on unsupported systems
Fixes #1494
2020-02-10 19:09:03 -05:00
Zachary Yedidia
e1e310a96e Document all options 2020-02-10 15:07:00 -05:00
Zachary Yedidia
d29b941be0 Merge 2020-02-10 14:59:47 -05:00
Zachary Yedidia
cde696915d Merge branch 'sum01-issue_1008' 2020-02-10 14:59:40 -05:00
Zachary Yedidia
185b8de17b Merge branch 'issue_1008' of https://github.com/sum01/micro into sum01-issue_1008 2020-02-10 14:59:31 -05:00
Kyle Barron
d65285ee54 Update documentation to include Material colorscheme (#1279) 2020-02-10 14:53:27 -05:00
Zachary Yedidia
848bd1ba8c Fix rehighlight for retab 2020-02-10 14:49:08 -05:00
Zachary Yedidia
00205aa6a7 Update readme 2020-02-10 00:38:57 -05:00
Zachary Yedidia
ecb9fd5a8a Change diffgutter default to false 2020-02-10 00:30:13 -05:00
Zachary Yedidia
bdf9e6d3a4 Merge branch 'diff-gutter' of https://github.com/p-e-w/micro 2020-02-10 00:28:43 -05:00
Zachary Yedidia
3ed77dbb2e Sanitize inputs to insert and remove 2020-02-10 00:18:08 -05:00
Zachary Yedidia
57a992c4a3 Merge 2020-02-09 22:40:20 -05:00
Zachary Yedidia
c63614213d Update tcell dep 2020-02-09 22:40:18 -05:00
Rein F
b7a54fa74a updated man page (#1492) 2020-02-09 21:53:42 -05:00
Zachary Yedidia
63046ae909 Don't autocomplete in the middle of a word
Fixes #1490
2020-02-09 16:46:53 -05:00
Zachary Yedidia
af48e4b79b Fix save callbacks
Fixes #1491
2020-02-09 16:36:15 -05:00
Zachary Yedidia
4e73d0779b Create bindings.json if it does not exist 2020-02-09 16:27:39 -05:00
Zachary Yedidia
6f424f3213 Properly flush bufio writer 2020-02-09 15:36:31 -05:00
Zachary Yedidia
e110e93e0f Improve disk performance with buffered io 2020-02-09 15:21:23 -05:00
Zachary Yedidia
8ddf335e68 Improve remove performance 2020-02-09 14:58:37 -05:00
Philipp Emanuel Weidmann
de33eac058 Add diff gutter 2020-02-08 13:26:24 +05:30
prez
aae0f4630e Added option to automatically save files with sucmd 2019-09-11 15:38:15 +02:00
Jawahar Suresh Babu
c46695bb57 Fixed the issue #1049 - Endless prompt to reload changes 2019-02-12 07:20:07 +05:30
Hugo Locurcio
a732d03b4d Improve writing style and formatting in README 2018-08-07 14:35:07 +02:00
sum01
28267b9eb2 Add back ?
Accidentally removed
2018-02-06 13:41:56 -05:00
sum01
cad43914b0 Html syntax fixes #1008
Note that there's a TODO with if/when 'limit-rules' are added.
Till that's added, any 'style' and 'script' blocks will be missing highlighting on their identifiers.
The actual contents (CSS or JS) will still work correctly though.
2018-02-05 23:13:57 -05:00
121 changed files with 5443 additions and 1566 deletions

View File

@@ -2,7 +2,7 @@
## Specifications
You can use `micro -version` to get the commit hash.
<!-- You can use `micro -version` to get the commit hash. -->
Commit hash:
OS:

2
.gitignore vendored
View File

@@ -11,8 +11,10 @@ todo.txt
test.txt
log.txt
*.old
benchmark_results*
tools/build-version
tools/build-date
tools/info-plist
tools/bindata
tools/vscode-tests/
*.hdr

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016-2017: Zachary Yedidia, et al.
Copyright (c) 2016-2020: Zachary Yedidia, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -8,14 +8,19 @@ DATE = $(shell GOOS=$(shell go env GOHOSTOS) GOARCH=$(shell go env GOHOSTARCH) \
ADDITIONAL_GO_LINKER_FLAGS = $(shell GOOS=$(shell go env GOHOSTOS) \
GOARCH=$(shell go env GOHOSTARCH))
GOBIN ?= $(shell go env GOPATH)/bin
GOVARS = -X github.com/zyedidia/micro/internal/util.Version=$(VERSION) -X github.com/zyedidia/micro/internal/util.CommitHash=$(HASH) -X 'github.com/zyedidia/micro/internal/util.CompileDate=$(DATE)' -X github.com/zyedidia/micro/internal/util.Debug=OFF
GOVARS = -X github.com/zyedidia/micro/v2/internal/util.Version=$(VERSION) -X github.com/zyedidia/micro/v2/internal/util.CommitHash=$(HASH) -X 'github.com/zyedidia/micro/v2/internal/util.CompileDate=$(DATE)'
DEBUGVAR = -X github.com/zyedidia/micro/v2/internal/util.Debug=ON
VSCODE_TESTS_BASE_URL = 'https://raw.githubusercontent.com/microsoft/vscode/e6a45f4242ebddb7aa9a229f85555e8a3bd987e2/src/vs/editor/test/common/model/'
# Builds micro after checking dependencies but without updating the runtime
build:
go build -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
build-dbg:
go build -ldflags "-s -w $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
go build -ldflags "-s -w $(ADDITIONAL_GO_LINKER_FLAGS) $(DEBUGVAR)" ./cmd/micro
build-tags: fetch-tags
go build -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
# Builds micro after building the runtime and checking dependencies
build-all: runtime build
@@ -35,6 +40,9 @@ install-all: runtime install
install-quick:
go install -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
fetch-tags:
git fetch --tags
# Builds the runtime
runtime:
git submodule update --init
@@ -44,8 +52,34 @@ runtime:
mv runtime.go internal/config
gofmt -w internal/config/runtime.go
testgen:
mkdir -p tools/vscode-tests
cd tools/vscode-tests && \
curl --remote-name-all $(VSCODE_TESTS_BASE_URL){editableTextModelAuto,editableTextModel,model.line}.test.ts
tsc tools/vscode-tests/*.ts > /dev/null; true
go run tools/testgen.go tools/vscode-tests/*.js > buffer_generated_test.go
mv buffer_generated_test.go internal/buffer
gofmt -w internal/buffer/buffer_generated_test.go
test:
go test ./internal/...
bench:
for i in 1 2 3; do \
go test -bench=. ./internal/...; \
done > benchmark_results
benchstat benchmark_results
bench-baseline:
for i in 1 2 3; do \
go test -bench=. ./internal/...; \
done > benchmark_results_baseline
bench-compare:
for i in 1 2 3; do \
go test -bench=. ./internal/...; \
done > benchmark_results
benchstat benchmark_results_baseline benchmark_results
clean:
rm -f micro

187
README.md
View File

@@ -1,32 +1,35 @@
# ![Micro](./assets/logo.png)
<img alt="micro logo" src="./assets/micro-logo.svg" width="500px"/>
[![Build Status](https://travis-ci.org/zyedidia/micro.svg?branch=master)](https://travis-ci.org/zyedidia/micro)
[![Go Report Card](https://goreportcard.com/badge/github.com/zyedidia/micro)](https://goreportcard.com/report/github.com/zyedidia/micro)
[![Join the chat at https://gitter.im/zyedidia/micro](https://badges.gitter.im/zyedidia/micro.svg)](https://gitter.im/zyedidia/micro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Release](https://img.shields.io/github/release/zyedidia/micro.svg?label=Release)](https://github.com/zyedidia/micro/releases)
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/zyedidia/micro/blob/master/LICENSE)
[![Join the chat at https://gitter.im/zyedidia/micro](https://badges.gitter.im/zyedidia/micro.svg)](https://gitter.im/zyedidia/micro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Snap Status](https://build.snapcraft.io/badge/zyedidia/micro.svg)](https://build.snapcraft.io/user/zyedidia/micro)
Micro is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the full capabilities
of modern terminals. It comes as one single, batteries-included, static binary with no dependencies, and you can download and use it right now.
**micro** is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the capabilities
of modern terminals. It comes as a single, batteries-included, static binary with no dependencies; you can download and use it right now!
As the name indicates, micro aims to be somewhat of a successor to the nano editor by being easy to install and use in a pinch, but micro also aims to be
enjoyable to use full time, whether you work in the terminal because you prefer it (like me), or because you need to (over ssh).
As its name indicates, micro aims to be somewhat of a successor to the nano editor by being easy to install and use.
It strives to be enjoyable as a full-time editor for people who prefer to work in a terminal, or those who regularly edit files over SSH.
Here is a picture of micro editing its source code.
![Screenshot](./assets/micro-solarized.png)
To see more screenshots of micro, showcasing all of the default colorschemes, see [here](http://zbyedidia.webfactional.com/micro/screenshots.html).
To see more screenshots of micro, showcasing some of the default color schemes, see [here](http://zbyedidia.webfactional.com/micro/screenshots.html).
You can also check out the website for Micro at https://micro-editor.github.io.
# Table of Contents
## Table of Contents
- [Features](#features)
- [Installation](#installation)
- [Prebuilt binaries](#prebuilt-binaries)
- [Package Managers](#package-managers)
- [Building from source](#building-from-source)
- [MacOS terminal](#macos-terminal)
- [Fully static binary](#fully-static-binary)
- [macOS terminal](#macos-terminal)
- [Linux clipboard support](#linux-clipboard-support)
- [Colors and syntax highlighting](#colors-and-syntax-highlighting)
- [Plan9, Cygwin, Mingw](#plan9-cygwin-mingw)
@@ -36,35 +39,38 @@ You can also check out the website for Micro at https://micro-editor.github.io.
- - -
# Features
## Features
* Easy to use and to install
* No dependencies or external files are needed -- just the binary you can download further down the page
* Multiple cursors
* Common keybindings (ctrl-s, ctrl-c, ctrl-v, ctrl-z...)
* Keybindings can be rebound to your liking
* Sane defaults
* You shouldn't have to configure much out of the box (and it is extremely easy to configure)
* Splits and tabs
* Nano-like menu to help you remember the keybindings
* Extremely good mouse support
* This means mouse dragging to create a selection, double click to select by word, and triple click to select by line
* Cross platform (It should work on all the platforms Go runs on)
* Note that while Windows is supported Mingw/Cygwin is not (see below)
* Plugin system (plugins are written in Lua)
* Persistent undo
* Automatic linting and error notifications
* Syntax highlighting (for over [120 languages](runtime/syntax)!)
* Colorscheme support
* By default, micro comes with 16, 256, and true color themes.
* True color support (set the `MICRO_TRUECOLOR` environment variable to 1 to enable it)
* Copy and paste with the system clipboard
* Small and simple
* Easily configurable
* Macros
* Common editor things such as undo/redo, line numbers, Unicode support, softwrap...
- Easy to use and install.
- No dependencies or external files are needed just the binary you can download further down the page.
- Multiple cursors.
- Common keybindings (<kbd>Ctrl+S</kbd>, <kbd>Ctrl+C</kbd>, <kbd>Ctrl+V</kbd>, <kbd>Ctrl+Z</kbd>, …).
- Keybindings can be rebound to your liking.
- Sane defaults.
- You shouldn't have to configure much out of the box (and it is extremely easy to configure).
- Splits and tabs.
- nano-like menu to help you remember the keybindings.
- Extremely good mouse support.
- This means mouse dragging to create a selection, double click to select by word, and triple click to select by line.
- Cross-platform (it should work on all the platforms Go runs on).
- Note that while Windows is supported Mingw/Cygwin is not (see below)
- Plugin system (plugins are written in Lua).
- micro has a built-in plugin manager to automatically install, remove, and update plugins.
- Built-in diff gutter
- Simple autocompletion
- Persistent undo.
- Automatic linting and error notifications
- Syntax highlighting for over [130 languages](runtime/syntax).
- Color scheme support.
- By default, micro comes with 16, 256, and true color themes.
- True color support (set the `MICRO_TRUECOLOR` environment variable to 1 to enable it).
- Copy and paste with the system clipboard.
- Small and simple.
- Easily configurable.
- Macros.
- Common editor features such as undo/redo, line numbers, Unicode support, soft wrapping, …
# Installation
## Installation
To install micro, you can download a [prebuilt binary](https://github.com/zyedidia/micro/releases), or you can build it from source.
@@ -79,19 +85,19 @@ Download the binary from the [releases](https://github.com/zyedidia/micro/releas
On that page you'll see the nightly release, which contains binaries for micro which are built every night,
and you'll see all the stable releases with the corresponding binaries.
If you'd like to see more information after installing micro, run `micro -version`.
Running `micro -version` will give you the version information.
### Installation script
There is a great script which can install micro for you by downloading the latest prebuilt binary. You can find it at https://getmic.ro (the github repo for it is [here](https://github.com/benweissmann/getmic.ro)).
There is a script which can install micro for you by downloading the latest prebuilt binary. You can find it at <https://getmic.ro>.
Then you can easily install micro:
You can easily install micro by running
$ curl https://getmic.ro | bash
```bash
curl https://getmic.ro | bash
```
The script will install the micro binary to the current directory.
See the [Github page](https://github.com/benweissmann/getmic.ro) for more information.
The script will install the micro binary to the current directory. See its [GitHub repository](https://github.com/benweissmann/getmic.ro) for more information.
### Package managers
@@ -101,13 +107,26 @@ You can install micro using Homebrew on Mac:
brew install micro
```
On Debian Linux, you can install micro through [snap](https://snapcraft.io/docs/core/install)
**Note for Mac:** All micro keybindings use the control or alt (option) key, not the command
key. By default, macOS terminals do not forward alt key events. To fix this, please see
the section on [macOS terminals](https://github.com/zyedidia/micro#macos-terminal) further below.
On Linux, you can install micro through [snap](https://snapcraft.io/docs/core/install)
```
snap install micro --classic
```
Homebrew and snap are the two "officially" maintained package manager distributions of micro.
On Debian `unstable | testing | buster-backports` and Ubuntu `focal` (20.04), micro is available
via `apt`:
```
sudo apt install micro
```
**Note for Linux:** for interfacing with the local system clipboard, `xclip` or `xsel`
must be installed. Please see the section on [Linux clipboard support](https://github.com/zyedidia/micro#linux-clipboard-support)
further below.
Micro is also available through other package managers on Linux such as AUR, Nix, and package managers
for other operating systems:
@@ -117,6 +136,8 @@ for other operating systems:
* `scoop install micro`
* OpenBSD: Available in the ports tree and also available as a binary package
* `pkd_add -v micro`
* Arch Linux, CRUX, Termux for Android
* See details in the [wiki page](https://github.com/zyedidia/micro/wiki/Installing-Micro)
### Building from source
@@ -137,55 +158,67 @@ anywhere you like (for example `/usr/local/bin`).
The command `make install` will install the binary to `$GOPATH/bin` or `$GOBIN`.
You can install directly with `go get` (`go get github.com/zyedidia/micro/cmd/micro`) but this isn't
recommended because it doesn't build micro with version information, and doesn't disable debug mode.
recommended because it doesn't build micro with version information (necessary for the plugin manager),
and doesn't disable debug mode.
### MacOS terminal
### Fully static binary
If you are using MacOS, you should consider using [iTerm2](http://iterm2.com/) instead of the default Mac terminal. The iTerm2 terminal has much better mouse support as well as better handling of key events. For best keybinding behavior, choose `xterm defaults` under `Preferences->Profiles->Keys->Load Preset`. The newest versions also support true color.
By default, the micro binary will dynamically link with core system libraries (this is generally
recommended for security and portability). However, there is a fully static prebuilt binary that
is provided for amd64 as `linux-static.tar.gz`, and to build a fully static binary from source, run
```
CGO_ENABLED=0 make build
```
### macOS terminal
If you are using macOS, you should consider using [iTerm2](http://iterm2.com/) instead of the default terminal (Terminal.app). The iTerm2 terminal has much better mouse support as well as better handling of key events. For best keybinding behavior, choose `xterm defaults` under `Preferences->Profiles->Keys->Load Preset`. The newest versions also support true color.
If you still insist on using the default Mac terminal, be sure to set `Use Option key as Meta key` under
`Preferences->Profiles->Keyboard` to use <kbd>option</kbd> as <kbd>alt</kbd>.
### Linux clipboard support
On Linux, clipboard support requires the 'xclip' or 'xsel' commands to be installed.
On Linux, clipboard support requires:
For Ubuntu:
- On X11, the `xclip` or `xsel` commands (for Ubuntu: `sudo apt install xclip`)
- On Wayland, the `wl-clipboard` command
```sh
sudo apt-get install xclip
```
If you don't have xclip or xsel, micro will use an internal clipboard for copy and paste, but it won't work with external applications.
If you don't have these commands, micro will use an internal clipboard for copy and paste, but it won't work with external applications.
### Colors and syntax highlighting
If you open micro and it doesn't seem like syntax highlighting is working, this is probably because
you are using a terminal which does not support 256 colors. Try changing the colorscheme to `simple`
by pressing CtrlE in micro and typing `set colorscheme simple`.
you are using a terminal which does not support 256 color mode. Try changing the color scheme to `simple`
by pressing <kbd>Ctrl+E</kbd> in micro and typing `set colorscheme simple`.
If you are using the default Ubuntu terminal, to enable 256 make sure your `TERM` variable is set
to `xterm-256color`.
Many older Windows terminals don't support more than 16 colors, which means
that micro's default colorscheme won't look very good. You can either set
the colorscheme to `simple`, or download a better terminal emulator, like
mintty. However, if you are on a recent version of Windows 10, the default
`cmd.exe` should do fine.
Many of the Windows terminals don't support more than 16 colors, which means
that micro's default color scheme won't look very good. You can either set
the color scheme to `simple`, or download and configure a better terminal emulator
than the Windows default.
### Plan9, Cygwin, Mingw
### Cygwin, Mingw, Plan9
These platforms are unfortunately not supported.
Cygwin, Mingw, and Plan9 are unfortunately not officially supported. In Cygwin and Mingw, micro will often work when run using
the `winpty` utility:
```
winpty micro.exe ...
```
Micro uses the amazing [tcell library](https://github.com/gdamore/tcell), but this
means that micro is restricted to the platforms tcell supports. As a result, micro does not support
Plan9, and Cygwin (although this may change in the future). Micro also doesn't support NaCl (but NaCl is deprecated anyways).
Plan9, and Cygwin (although this may change in the future). Micro also doesn't support NaCl (which is deprecated anyway).
# Usage
## Usage
Once you have built the editor, simply start it by running `micro path/to/file.txt` or simply `micro` to open an empty buffer.
Once you have built the editor, start it by running `micro path/to/file.txt` or `micro` to open an empty buffer.
Micro also supports creating buffers from `stdin`:
micro also supports creating buffers from `stdin`:
```sh
ifconfig | micro
@@ -197,22 +230,22 @@ You can also use the mouse to manipulate the text. Simply clicking and dragging
will select text. You can also double click to enable word selection, and triple
click to enable line selection.
# Documentation and Help
## Documentation and Help
Micro has a built-in help system which you can access by pressing `Ctrl-E` and typing `help`. Additionally, you can
micro has a built-in help system which you can access by pressing <kbd>Ctrl+E</kbd> and typing `help`. Additionally, you can
view the help files here:
* [main help](https://github.com/zyedidia/micro/tree/master/runtime/help/help.md)
* [keybindings](https://github.com/zyedidia/micro/tree/master/runtime/help/keybindings.md)
* [commands](https://github.com/zyedidia/micro/tree/master/runtime/help/commands.md)
* [colors](https://github.com/zyedidia/micro/tree/master/runtime/help/colors.md)
* [options](https://github.com/zyedidia/micro/tree/master/runtime/help/options.md)
* [plugins](https://github.com/zyedidia/micro/tree/master/runtime/help/plugins.md)
- [main help](https://github.com/zyedidia/micro/tree/master/runtime/help/help.md)
- [keybindings](https://github.com/zyedidia/micro/tree/master/runtime/help/keybindings.md)
- [commands](https://github.com/zyedidia/micro/tree/master/runtime/help/commands.md)
- [colors](https://github.com/zyedidia/micro/tree/master/runtime/help/colors.md)
- [options](https://github.com/zyedidia/micro/tree/master/runtime/help/options.md)
- [plugins](https://github.com/zyedidia/micro/tree/master/runtime/help/plugins.md)
I also recommend reading the [tutorial](https://github.com/zyedidia/micro/tree/master/runtime/help/tutorial.md) for
a brief introduction to the more powerful configuration features micro offers.
# Contributing
## Contributing
If you find any bugs, please report them! I am also happy to accept pull requests from anyone.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg3336"
version="1.1"
inkscape:version="0.91 r13725"
width="128"
height="128"
viewBox="0 0 128 128"
sodipodi:docname="logo.svg">
<metadata
id="metadata3342">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3340" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1355"
inkscape:window-height="717"
id="namedview3338"
showgrid="false"
inkscape:zoom="1.6243169"
inkscape:cx="111.32302"
inkscape:cy="30.538264"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3336" />
<path
style="fill:#2e3192;fill-opacity:1"
d="m 56.1,127.32358 c -13.68932,-1.70993 -27.156628,-8.3544 -37.112903,-18.31068 -25.0687936,-25.068788 -25.0687936,-65.95701 0,-91.025803 25.068793,-25.0687936 65.957015,-25.0687936 91.025803,0 25.0688,25.068793 25.0688,65.957015 0,91.025803 C 95.87457,123.15123 76.198116,129.83404 56.1,127.32358 Z"
id="path3364"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff"
d="m 40.756452,106.01908 c 1.442831,-1.83426 1.55476,-4.09687 0.414499,-8.37899 -0.678184,-2.546844 -0.684604,-4.05591 -0.03829,-9 1.276867,-9.767604 4.483143,-23.040636 5.565559,-23.039766 0.220979,1.74e-4 0.417725,2.092674 0.437213,4.65 0.04167,5.468298 1.558564,9.06891 4.638769,11.010942 2.551646,1.608774 9.15365,1.329324 12.80399,-0.541974 3.245124,-1.663572 7.649064,-6.112434 9.850956,-9.951438 L 76.188736,67.7 l 0.0054,3.922866 c 0.0042,2.867148 0.36894,4.642788 1.355628,6.59796 1.532058,3.035856 3.323226,4.15755 6.659322,4.17033 5.192928,0.01986 9.07014,-3.668676 10.866768,-10.338036 0.98277,-3.64821 1.064448,-11.21265 0.09235,-8.55312 -3.025218,8.276592 -4.468212,9.893562 -9.238056,10.351884 -2.629152,0.25263 -3.177804,0.08883 -4.921776,-1.469412 -1.609044,-1.437678 -2.016072,-2.308416 -2.258508,-4.8315 -0.262884,-2.73585 0.105942,-4.06497 3.32007,-11.964365 C 88.28388,40.315087 89.33625,35.536248 87,33.2 c -1.559352,-1.559353 -3.62787,-1.522741 -5.691792,0.10074 -2.295762,1.805846 -3.105984,4.070756 -5.14293,14.376662 -2.464164,12.46744 -6.525822,20.297092 -12.62193,24.331306 C 59.052142,74.98085 52.704914,73.6403 50.637191,69.282896 49.19967,66.253544 49.857706,62.552972 53.387813,53.814319 56.613526,45.829186 58.8,38.711369 58.8,36.195564 c 0,-4.161283 -4.366993,-5.665719 -7.364438,-2.537061 -2.183558,2.279144 -3.117251,5.256959 -4.280897,13.653016 -0.547956,3.953665 -1.259292,9.010489 -1.580746,11.237387 -0.321454,2.226896 -2.083918,8.706896 -3.916587,14.400002 -4.33165,13.456074 -6.85029,23.184822 -7.273674,28.096022 -0.325586,3.77675 -0.269352,4.00056 1.319044,5.25 2.187498,1.72068 3.541408,1.64679 5.05375,-0.27585 z"
id="path3362"
inkscape:connector-curvature="0" />
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

70
assets/micro-logo.svg Normal file
View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 299.89999 103.2"
enable-background="new 0 0 960 560"
xml:space="preserve"
inkscape:version="0.91 r13725"
sodipodi:docname="micro-logo.svg"
width="299.89999"
height="103.2"><metadata
id="metadata21"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs19" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1237"
inkscape:window-height="867"
id="namedview17"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.1416667"
inkscape:cx="75.655934"
inkscape:cy="-4"
inkscape:window-x="1097"
inkscape:window-y="185"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" /><g
id="g3"
transform="translate(-178,-172.8)"><path
d="m 306.8,213.8 0,-2.6 c 1.6,-0.1 2.9,-0.4 4.1,-0.8 1.2,-0.4 2.5,-1 4,-1.8 l 2.3,0 0,5.2 c 2.4,-1.9 4.2,-3.1 5.5,-3.8 2,-1 4,-1.5 5.8,-1.5 1.3,0 2.5,0.2 3.7,0.7 1.2,0.5 2.2,1 2.9,1.7 0.7,0.7 1.4,1.6 1.9,2.8 2.2,-1.9 4.2,-3.3 6,-4 1.9,-0.8 3.7,-1.2 5.6,-1.2 1.8,0 3.4,0.4 4.8,1.1 1.4,0.8 2.4,1.7 3,2.8 0.6,1.1 0.9,2.8 0.9,5 l 0,14.4 c 0,1.5 0,2.4 0.1,2.6 0.1,0.4 0.3,0.8 0.7,1.1 0.3,0.4 0.7,0.6 1.2,0.7 0.4,0.1 1.2,0.2 2.4,0.2 l 1,0 0,2.6 -15.5,0 0,-2.6 c 1.8,0 2.9,-0.1 3.5,-0.4 0.5,-0.2 0.9,-0.6 1.2,-1.2 0.3,-0.6 0.4,-1.6 0.4,-3.2 l 0,-13.7 c 0,-1.7 -0.2,-2.9 -0.5,-3.6 -0.3,-0.7 -0.9,-1.2 -1.7,-1.7 -0.8,-0.4 -1.8,-0.7 -3,-0.7 -1.5,0 -3,0.4 -4.6,1.2 -2.2,1.1 -3.9,2.3 -5.1,3.6 l 0,14.8 c 0,1.4 0.1,2.4 0.2,2.8 0.1,0.4 0.4,0.8 0.7,1.1 0.3,0.3 0.7,0.5 1.1,0.6 0.4,0.1 1.5,0.2 3.1,0.2 l 0,2.6 -15.3,0 0,-2.6 0.9,0 c 1.2,0 2.1,-0.1 2.6,-0.4 0.5,-0.3 0.9,-0.7 1.2,-1.3 0.2,-0.5 0.3,-1.5 0.3,-2.9 l 0,-13.2 c 0,-1.9 -0.2,-3.3 -0.5,-3.9 -0.3,-0.7 -0.9,-1.3 -1.7,-1.7 -0.8,-0.5 -1.8,-0.7 -3,-0.7 -1.3,0 -2.7,0.3 -4.1,1 -2,1 -3.9,2.2 -5.6,3.8 l 0,15.9 c 0,1 0.1,1.6 0.4,2.1 0.3,0.4 0.7,0.8 1.2,1.1 0.6,0.3 1.3,0.4 2.3,0.4 l 1.1,0 0,2.6 -15.6,0 0,-2.6 0.8,0 c 1.4,0 2.4,-0.1 2.8,-0.3 0.7,-0.3 1.1,-0.8 1.4,-1.5 0.2,-0.4 0.2,-1.3 0.2,-2.9 l 0,-18.1 -5.1,0 z"
id="path5"
inkscape:connector-curvature="0" /><path
d="m 366.4,213.7 0,-2.6 c 1.7,-0.2 3.2,-0.5 4.3,-0.9 1.2,-0.4 2.5,-1 4,-1.7 l 2.3,0 0,24.9 c 0,0.9 0.1,1.5 0.4,2 0.2,0.4 0.6,0.8 1,0.9 0.4,0.2 1.3,0.3 2.4,0.3 l 1.5,0 0,2.6 -15.9,0 0,-2.6 1.3,0 c 1.4,0 2.3,-0.1 2.8,-0.4 0.5,-0.2 0.8,-0.6 1,-1.1 0.2,-0.5 0.3,-1.5 0.3,-3.2 l 0,-18.3 -5.4,0 z m 7.9,-19.2 c 1,0 1.8,0.3 2.5,1 0.7,0.7 1.1,1.5 1.1,2.5 0,1 -0.4,1.8 -1.1,2.5 -0.7,0.7 -1.6,1.1 -2.5,1.1 -1,0 -1.8,-0.4 -2.5,-1.1 -0.7,-0.7 -1.1,-1.6 -1.1,-2.5 0,-1 0.4,-1.8 1.1,-2.5 0.6,-0.6 1.5,-1 2.5,-1 z"
id="path7"
inkscape:connector-curvature="0" /><path
d="m 413.1,230.6 2,1.6 c -3.9,5.2 -8.6,7.8 -14,7.8 -4.2,0 -7.8,-1.5 -10.7,-4.5 -2.9,-3 -4.4,-6.8 -4.4,-11.3 0,-3 0.7,-5.7 2,-8.1 1.3,-2.4 3.2,-4.2 5.6,-5.6 2.4,-1.3 5.2,-2 8.3,-2 3.6,0 6.5,0.9 8.9,2.6 2.4,1.7 3.6,3.5 3.6,5.3 0,1 -0.3,1.7 -0.8,2.2 -0.5,0.5 -1.2,0.8 -1.9,0.8 -0.4,0 -0.7,-0.1 -1.1,-0.3 -0.4,-0.2 -0.7,-0.5 -1.1,-0.9 -0.2,-0.2 -0.5,-0.8 -0.9,-1.7 -0.6,-1.2 -1,-2 -1.3,-2.4 -0.6,-0.8 -1.4,-1.5 -2.4,-2 -0.9,-0.5 -2,-0.7 -3.1,-0.7 -1.8,0 -3.4,0.5 -4.9,1.5 -1.5,1 -2.7,2.4 -3.6,4.3 -0.9,1.9 -1.3,4.2 -1.3,6.8 0,4.1 1.1,7.3 3.3,9.7 1.9,2.1 4.1,3.1 6.7,3.1 1.2,0 2.4,-0.2 3.6,-0.6 1.2,-0.4 2.4,-1 3.5,-1.8 0.9,-0.6 2.2,-1.8 4,-3.8 z"
id="path9"
inkscape:connector-curvature="0" /><path
d="m 418.7,213.7 0,-2.6 c 1.5,-0.1 2.8,-0.4 4,-0.8 1.2,-0.4 2.5,-1 4,-1.9 l 2.3,0 0,5.9 c 1.5,-1.8 3.2,-3.2 5.1,-4.3 1.9,-1.1 3.7,-1.6 5.2,-1.6 1.5,0 2.7,0.4 3.6,1.1 0.9,0.7 1.3,1.6 1.3,2.6 0,0.7 -0.3,1.4 -0.9,2 -0.6,0.6 -1.3,0.9 -2.1,0.9 -0.4,0 -0.7,-0.1 -1,-0.2 -0.3,-0.1 -0.7,-0.3 -1.2,-0.7 -1.1,-0.7 -2.1,-1.1 -2.9,-1.1 -1,0 -2.2,0.4 -3.4,1.3 -1.6,1.1 -2.8,2.2 -3.7,3.3 l 0,14.4 c 0,1.2 0.1,2.1 0.2,2.5 0.1,0.4 0.4,0.8 0.7,1.1 0.3,0.3 0.7,0.6 1.1,0.7 0.5,0.1 1.3,0.2 2.4,0.2 l 1,0 0,2.6 -16,0 0,-2.6 1.3,0 c 1.3,0 2.1,-0.1 2.5,-0.3 0.5,-0.3 0.9,-0.7 1.2,-1.3 0.3,-0.5 0.4,-1.5 0.4,-3 l 0,-18.3 -5.1,0 z"
id="path11"
inkscape:connector-curvature="0" /><path
d="m 462.8,208.5 c 3,0 5.7,0.6 7.9,1.9 2.2,1.3 4,3.1 5.3,5.5 1.3,2.4 1.9,5.2 1.9,8.3 0,3.1 -0.7,5.9 -2,8.3 -1.3,2.4 -3.1,4.3 -5.4,5.5 -2.3,1.3 -5,1.9 -8.1,1.9 -5,0 -8.8,-1.6 -11.3,-4.7 -2.5,-3.1 -3.8,-6.8 -3.8,-11 0,-3.1 0.7,-5.8 2,-8.2 1.3,-2.4 3.1,-4.2 5.5,-5.6 2.4,-1.2 5.1,-1.9 8,-1.9 z m -0.2,3 c -2.4,0 -4.4,0.9 -6,2.8 -2.1,2.3 -3.1,5.7 -3.1,10.1 0,4.3 0.9,7.5 2.6,9.7 1.6,2 3.8,3 6.5,3 1.8,0 3.3,-0.5 4.7,-1.4 1.4,-0.9 2.5,-2.4 3.3,-4.5 0.8,-2 1.3,-4.4 1.3,-7.2 0,-2.7 -0.5,-5.1 -1.4,-7.2 -0.7,-1.7 -1.8,-3 -3.2,-4 -1.3,-0.8 -2.9,-1.3 -4.7,-1.3 z"
id="path13"
inkscape:connector-curvature="0" /></g><path
d="M 51.6,0 C 23.1,0 0,23.1 0,51.6 c 0,28.5 23.1,51.6 51.6,51.6 28.5,0 51.6,-23.1 51.6,-51.6 C 103.2,23.1 80.1,0 51.6,0 Z m 24.5,58.6 c -0.5,2 -1.3,3.6 -2.4,4.9 -1,1.3 -2,2.1 -3.1,2.5 -1.1,0.4 -2.2,0.6 -3.4,0.6 -1.2,0 -2.2,-0.2 -3,-0.7 C 63.4,65.5 62.8,64.8 62.3,64 61.8,63.2 61.5,62.2 61.3,61.1 61.1,60 61,58.8 61,57.5 c 0,-0.5 0,-1 0.1,-1.7 0.1,-0.7 0.2,-1.6 0.3,-1.6 l -0.2,0 c -1.6,4 -3.8,6.9 -6.6,9.2 -2.8,2.3 -5.9,3.4 -9.3,3.4 -2.3,0 -4.2,-0.9 -5.5,-2.6 -1.4,-1.7 -2.1,-4.3 -2.1,-7.7 0,-0.5 0,-1 0.1,-1.6 0.1,-0.5 0.1,-0.7 0.2,-1.7 l -0.7,0 c -0.9,2 -1.7,4.8 -2.3,7.3 -0.6,2.5 -1.1,4.8 -1.4,6.9 -0.4,2.1 -0.6,4 -0.8,5.6 -0.2,1.6 -0.3,2.7 -0.4,3.3 0.1,0.5 0.2,1 0.3,1.6 0.2,0.6 0.3,1.2 0.5,1.7 0.2,0.5 0.3,1.1 0.4,1.6 0.1,0.5 0.2,0.9 0.2,1.2 0,1.4 -0.3,2.5 -0.9,3.2 -0.6,0.7 -1.3,1.1 -2,1.1 -0.9,0 -1.7,-0.3 -2.3,-0.8 -0.7,-0.6 -1,-1.5 -1,-2.7 0,-1.7 0.3,-3.9 0.9,-6.5 0.6,-2.6 1.5,-5.9 2.6,-9.8 0.6,-1.8 1.1,-3.6 1.7,-5.4 0.6,-1.8 1.1,-3.5 1.6,-5 0.5,-1.5 0.9,-2.9 1.3,-4.1 0.4,-1.2 0.6,-2.1 0.7,-2.8 0.1,-0.3 0.2,-1 0.3,-2 0.1,-1 0.2,-2.1 0.4,-3.4 0.2,-1.3 0.3,-2.7 0.5,-4.1 0.2,-1.5 0.4,-2.8 0.5,-4 0.2,-0.9 0.3,-1.9 0.5,-3 0.2,-1.1 0.5,-2.2 0.9,-3.1 0.4,-1 1,-1.8 1.7,-2.5 0.7,-0.7 1.6,-1 2.7,-1 1.2,0 2,0.4 2.4,1.1 0.4,0.7 0.6,1.6 0.5,2.6 -0.1,1 -0.2,2.1 -0.5,3.2 -0.3,1.1 -0.6,2.1 -0.9,2.9 -0.8,2.5 -1.6,4.8 -2.5,6.7 -0.9,1.9 -1.7,4 -2.4,6.2 -0.6,1.5 -0.8,2.9 -0.8,4.1 0,2.2 0.7,3.8 2,5 1.4,1.2 3,1.7 4.9,1.7 1.5,0 3,-0.5 4.4,-1.6 1.4,-1.1 2.7,-2.4 3.9,-3.9 1.2,-1.5 2.2,-3.1 3,-4.9 0.8,-1.7 1.4,-3.3 1.8,-4.6 0.1,-0.2 0.2,-0.6 0.3,-1.4 0.2,-0.8 0.3,-1.7 0.5,-2.7 0.2,-1 0.4,-2 0.6,-3.1 0.2,-1.1 0.4,-2 0.5,-2.7 0.2,-0.8 0.3,-1.6 0.5,-2.6 0.2,-1 0.5,-1.9 0.9,-2.8 0.4,-0.9 1,-1.6 1.6,-2.2 0.7,-0.6 1.5,-0.9 2.6,-0.9 1.3,0 2.1,0.4 2.6,1.1 0.4,0.7 0.6,1.6 0.6,2.6 -0.1,1 -0.2,2 -0.5,3 -0.3,1 -0.5,1.8 -0.7,2.4 -0.8,2.5 -1.6,4.7 -2.4,6.7 -0.8,2 -1.5,3.8 -2.2,5.2 -0.6,1.5 -1.1,2.6 -1.5,3.5 -0.4,0.9 -0.6,1.5 -0.6,1.8 0,2.6 0.6,4.5 1.7,5.6 1.1,1.1 2.3,1.7 3.6,1.7 2.2,0 3.9,-0.7 5.2,-2 1.3,-1.4 2.3,-3.9 2.9,-6.9 l 0.8,0 c 0.2,2.9 -0.1,5.3 -0.6,7.3 z"
id="path15"
inkscape:connector-curvature="0"
style="fill:#2e3192" /></svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 253 KiB

View File

@@ -1,11 +1,4 @@
.\" micro manual page - micro(1)
.\"
.\" Copyright © 2017 Zachary Yedidia <zyedidia@gmail.com>
.\" Copyright © 2017 Collin Warren <anatoly@somethinghub.com>
.\"
.\" This document is provided under the same licensing as micro.
.\" See \usr\share\doc\micro\LICENSE for more information.
.TH micro 1 "2017-03-28"
.TH micro 1 "2020-02-10"
.SH NAME
micro \- A modern and intuitive terminal-based text editor
.SH SYNOPSIS
@@ -22,39 +15,96 @@ As the name indicates, micro aims to be somewhat of a successor to the nano edit
enjoyable to use full time, whether you work in the terminal because you prefer it (like me), or because you need to (over ssh).
.SH OPTIONS
.PP
\-clean
.RS 4
Cleans the configuration directory
.RE
.PP
\-config-dir dir
.RS 4
Specify a custom location for the configuration directory
.RE
.PP
\-startpos LINE,COL
[FILE]:LINE:COL
.RS 4
Specify a line and column to start the cursor at when opening a buffer
.RE
.PP
\-options
.RS 4
Show all option help
.RE
.PP
\-debug
.RS 4
Enable debug mode (enables logging to ./log.txt)
.RE
.PP
\-version
.RS 4
Show the version number and information
.RE
Micro's plugin's can be managed at the command line with the following commands.
.RS 4
.PP
\-plugin remove [PLUGIN]...
.RS 4
Remove plugin(s)
.RE
.PP
\-plugin update [PLUGIN]...
.RS 4
Update plugin(s) (if no argument is given, updates all plugins)
.RE
.PP
\-plugin search [PLUGIN]...
.RS 4
Search for a plugin
.RE
.PP
\-plugin list
.RS 4
List installed plugins
.RE
.PP
\-plugin available
.RS 4
List available plugins
.RE
.RE
Micro's options can also be set via command line arguments for quick
adjustments. For real configuration, please use the settings.json
file (see 'help options').
.RS 4
.PP
\-option value
.RS 4
Set `option` to `value` for this session
For example: `micro -syntax off file.c`
.RE
.SH CONFIGURATION
Micro uses
\fI$XDG_CONFIG_HOME/micro\fR
for configuration by default. If it is not set, micro uses ~/.config/micro.
Two main configuration files are settings.json, containing the user's
settings, and bindings.json, containing the user's custom keybindings.
.SH ENVIRONMENT
Micro's behaviour can be changed by setting environment variables, of which there is currently only one:
\fIMICRO_TRUECOLOR\fR.
When MICRO_TRUECOLOR is set to 1, micro will attempt to treat your terminal as a true-color terminal and will be able to make full use of the true-color colorschemes that are included with micro. If MICRO_TRUECOLOR is not set or is set to 0, then micro will only make use of 256 color features and will internally map true-color colorschemes to the nearest colors available. For more information see micro's documentation.
Micro uses $MICRO_CONFIG_HOME as the configuration directory.
If this environment variable is not set, it uses $XDG_CONFIG_HOME/micro instead.
If that environment variable is not set, it uses ~/.config/micro as the configuration directory.
In the documentation, we use ~/.config/micro to refer to the configuration directory
(even if it may in fact be somewhere else if you have set either of the above environment variables).
.SH NOTICE
This manpage is intended only to serve as a quick guide to the invocation of
@@ -69,5 +119,5 @@ and to report any newly encountered bugs you may find. We strive to correct
bugs as swiftly as possible.
.SH COPYRIGHT
Copyright \(co 2017 Zachary Yedidia, Collin Warren, et al.
Copyright \(co 2020 Zachary Yedidia, et al.
See /usr/share/doc/micro/LICENSE and /usr/share/doc/micro/AUTHORS for more information.

View File

@@ -10,8 +10,8 @@ import (
"sort"
"strings"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
)
func shouldContinue() bool {

View File

@@ -4,7 +4,7 @@ import (
"log"
"os"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/util"
)
// NullWriter simply sends writes into the void

View File

@@ -6,14 +6,14 @@ import (
lua "github.com/yuin/gopher-lua"
luar "layeh.com/gopher-luar"
"github.com/zyedidia/micro/internal/action"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/display"
ulua "github.com/zyedidia/micro/internal/lua"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/shell"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/action"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/display"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
)
func init() {

View File

@@ -5,18 +5,20 @@ import (
"fmt"
"io/ioutil"
"os"
"regexp"
"runtime"
"sort"
"time"
"github.com/go-errors/errors"
isatty "github.com/mattn/go-isatty"
"github.com/zyedidia/micro/internal/action"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/shell"
"github.com/zyedidia/micro/internal/util"
lua "github.com/yuin/gopher-lua"
"github.com/zyedidia/micro/v2/internal/action"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
)
@@ -43,6 +45,7 @@ func InitFlags() {
fmt.Println("-config-dir dir")
fmt.Println(" \tSpecify a custom location for the configuration directory")
fmt.Println("[FILE]:LINE:COL")
fmt.Println("+LINE:COL")
fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
fmt.Println("-options")
fmt.Println(" \tShow all option help")
@@ -146,11 +149,32 @@ func LoadInput() []*buffer.Buffer {
args := flag.Args()
buffers := make([]*buffer.Buffer, 0, len(args))
if len(args) > 0 {
btype := buffer.BTDefault
if !isatty.IsTerminal(os.Stdout.Fd()) {
btype = buffer.BTStdout
}
files := make([]string, 0, len(args))
flagStartPos := ""
flagr := regexp.MustCompile(`^\+\d+(:\d+)?$`)
for _, a := range args {
if flagr.MatchString(a) {
flagStartPos = a[1:]
} else {
if flagStartPos != "" {
files = append(files, a+":"+flagStartPos)
flagStartPos = ""
} else {
files = append(files, a)
}
}
}
if len(files) > 0 {
// Option 1
// We go through each file and load it
for i := 0; i < len(args); i++ {
buf, err := buffer.NewBufferFromFile(args[i], buffer.BTDefault)
for i := 0; i < len(files); i++ {
buf, err := buffer.NewBufferFromFile(files[i], btype)
if err != nil {
screen.TermMessage(err)
continue
@@ -167,17 +191,22 @@ func LoadInput() []*buffer.Buffer {
screen.TermMessage("Error reading from stdin: ", err)
input = []byte{}
}
buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault))
buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, btype))
} else {
// Option 3, just open an empty buffer
buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault))
buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, btype))
}
return buffers
}
func main() {
defer os.Exit(0)
defer func() {
if util.Stdout.Len() > 0 {
fmt.Fprint(os.Stdout, util.Stdout.String())
}
os.Exit(0)
}()
// runtime.SetCPUProfileRate(400)
// f, _ := os.Create("micro.prof")
@@ -218,9 +247,6 @@ func main() {
screen.Init()
// If we have an error, we can exit cleanly and not completely
// mess up the terminal being worked in
// In other words we need to shut down tcell before the program crashes
defer func() {
if err := recover(); err != nil {
screen.Screen.Fini()
@@ -280,52 +306,68 @@ func main() {
// clear the drawchan so we don't redraw excessively
// if someone requested a redraw before we started displaying
for len(screen.DrawChan) > 0 {
<-screen.DrawChan
for len(screen.DrawChan()) > 0 {
<-screen.DrawChan()
}
var event tcell.Event
// wait for initial resize event
select {
case event = <-events:
case event := <-events:
action.Tabs.HandleEvent(event)
case <-time.After(10 * time.Millisecond):
// time out after 10ms
}
// Since this loop is very slow (waits for user input every time) it's
// okay to be inefficient and run it via a function every time
// We do this so we can recover from panics without crashing the editor
for {
// Display everything
screen.Screen.Fill(' ', config.DefStyle)
screen.Screen.HideCursor()
action.Tabs.Display()
for _, ep := range action.MainTab().Panes {
ep.Display()
}
action.MainTab().Display()
action.InfoBar.Display()
screen.Screen.Show()
event = nil
// Check for new events
select {
case f := <-shell.Jobs:
// If a new job has finished while running in the background we should execute the callback
f.Function(f.Output, f.Args)
case <-config.Autosave:
for _, b := range buffer.OpenBuffers {
b.Save()
}
case <-shell.CloseTerms:
case event = <-events:
case <-screen.DrawChan:
}
if action.InfoBar.HasPrompt {
action.InfoBar.HandleEvent(event)
} else {
action.Tabs.HandleEvent(event)
}
DoEvent()
}
}
// DoEvent runs the main action loop of the editor
func DoEvent() {
var event tcell.Event
// recover from errors without crashing the editor
defer func() {
if err := recover(); err != nil {
if e, ok := err.(*lua.ApiError); ok {
screen.TermMessage("Lua API error:", e)
} else {
screen.TermMessage("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/zyedidia/micro/issues")
}
}
}()
// Display everything
screen.Screen.Fill(' ', config.DefStyle)
screen.Screen.HideCursor()
action.Tabs.Display()
for _, ep := range action.MainTab().Panes {
ep.Display()
}
action.MainTab().Display()
action.InfoBar.Display()
screen.Screen.Show()
// Check for new events
select {
case f := <-shell.Jobs:
// If a new job has finished while running in the background we should execute the callback
f.Function(f.Output, f.Args)
case <-config.Autosave:
for _, b := range buffer.OpenBuffers {
b.Save()
}
case <-shell.CloseTerms:
case event = <-events:
case <-screen.DrawChan():
}
if action.InfoBar.HasPrompt {
action.InfoBar.HandleEvent(event)
} else {
action.Tabs.HandleEvent(event)
}
}

8
go.mod
View File

@@ -1,4 +1,4 @@
module github.com/zyedidia/micro
module github.com/zyedidia/micro/v2
require (
github.com/blang/semver v3.5.1+incompatible
@@ -8,17 +8,19 @@ require (
github.com/mattn/go-isatty v0.0.11
github.com/mattn/go-runewidth v0.0.7
github.com/mitchellh/go-homedir v1.1.0
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff
github.com/sergi/go-diff v1.1.0
github.com/stretchr/testify v1.4.0
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb
github.com/zyedidia/clipboard v0.0.0-20190823154308-241f98e9b197
github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3
github.com/zyedidia/highlight v0.0.0-20170330143449-201131ce5cf5
github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d
github.com/zyedidia/pty v2.0.0+incompatible // indirect
github.com/zyedidia/tcell v1.4.2
github.com/zyedidia/tcell v1.4.5
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415
golang.org/x/text v0.3.2
gopkg.in/sourcemap.v1 v1.0.5 // indirect
gopkg.in/yaml.v2 v2.2.7
layeh.com/gopher-luar v1.0.7
)

12
go.sum
View File

@@ -29,6 +29,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff h1:+6NUiITWwE5q1KO6SAfUX918c+Tab0+tGAM/mtdlUyA=
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
@@ -40,6 +42,8 @@ github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/zyedidia/clipboard v0.0.0-20190823154308-241f98e9b197 h1:gYTNnAW6azuB3BbA6QYWO/H4F2ABSOjjw3Z03tlXd2c=
github.com/zyedidia/clipboard v0.0.0-20190823154308-241f98e9b197/go.mod h1:WDk3p8GiZV9+xFWlSo8qreeoLhW6Ik692rqXk+cNeRY=
github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834 h1:0nOfq3JwYRiY3+nwfWVQYEaXDmGCQgj3RKoqTifLzP4=
github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA=
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew=
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3/go.mod h1:YKbIYP//Eln8eDgAJGI3IDvR3s4Tv9Z9TGIOumiyQ5c=
github.com/zyedidia/highlight v0.0.0-20170330143449-201131ce5cf5 h1:Zs6mpwXvlqpF9zHl5XaN0p5V4J9XvP+WBuiuXyIgqvc=
@@ -50,8 +54,10 @@ github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s
github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE=
github.com/zyedidia/pty v2.0.0+incompatible h1:Ou5vXL6tvjst+RV8sUFISbuKDnUJPhnpygApMFGweqw=
github.com/zyedidia/pty v2.0.0+incompatible/go.mod h1:4y9l9yJZNxRa7GB/fB+mmDmGkG3CqmzLf4vUxGGotEA=
github.com/zyedidia/tcell v1.4.2 h1:JWMDs6O1saINPIR5M3kNqlWJwkfnBZeZDZszEJi3BW8=
github.com/zyedidia/tcell v1.4.2/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
github.com/zyedidia/tcell v1.4.4 h1:o34LXujNuSueuyTy+5eoQW+rQr8g0UbY8k1NczZyskQ=
github.com/zyedidia/tcell v1.4.4/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
github.com/zyedidia/tcell v1.4.5 h1:JFmOiWLxr3Fsk2vjRL3n8oRUoJeyrazGhkhZqW31kEY=
github.com/zyedidia/tcell v1.4.5/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 h1:752dTQ5OatJ9M5ULK2+9lor+nzyZz+LYDo3WGngg3Rc=
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415/go.mod h1:8leT8G0Cm8NoJHdrrKHyR9MirWoF4YW7pZh06B6H+1E=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -65,6 +71,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=

View File

@@ -5,15 +5,14 @@ import (
"runtime"
"strings"
"time"
"unicode/utf8"
shellquote "github.com/kballard/go-shellquote"
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/shell"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
)
@@ -175,7 +174,7 @@ func (h *BufPane) CursorRight() bool {
if tabstospaces && tabmovement {
tabsize := int(h.Buf.Settings["tabsize"].(float64))
line := h.Buf.LineBytes(h.Cursor.Y)
if h.Cursor.X+tabsize < utf8.RuneCount(line) && util.IsSpaces(line[h.Cursor.X:h.Cursor.X+tabsize]) && util.IsBytesWhitespace(line[0:h.Cursor.X]) {
if h.Cursor.X+tabsize < util.CharacterCount(line) && util.IsSpaces(line[h.Cursor.X:h.Cursor.X+tabsize]) && util.IsBytesWhitespace(line[0:h.Cursor.X]) {
for i := 0; i < tabsize; i++ {
h.Cursor.Right()
}
@@ -283,7 +282,7 @@ func (h *BufPane) SelectWordLeft() bool {
return true
}
// StartOfLine moves the cursor to the start of the text of the line
// StartOfText moves the cursor to the start of the text of the line
func (h *BufPane) StartOfText() bool {
h.Cursor.Deselect(true)
h.Cursor.StartOfText()
@@ -291,6 +290,19 @@ func (h *BufPane) StartOfText() bool {
return true
}
// StartOfTextToggle toggles the cursor between the start of the text of the line
// and the start of the line
func (h *BufPane) StartOfTextToggle() bool {
h.Cursor.Deselect(true)
if h.Cursor.IsStartOfText() {
h.Cursor.Start()
} else {
h.Cursor.StartOfText()
}
h.Relocate()
return true
}
// StartOfLine moves the cursor to the start of the line
func (h *BufPane) StartOfLine() bool {
h.Cursor.Deselect(true)
@@ -325,6 +337,22 @@ func (h *BufPane) SelectToStartOfText() bool {
return true
}
// SelectToStartOfTextToggle toggles the selection between the start of the text
// on the current line and the start of the line
func (h *BufPane) SelectToStartOfTextToggle() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
if h.Cursor.IsStartOfText() {
h.Cursor.Start()
} else {
h.Cursor.StartOfText()
}
h.Cursor.SelectTo(h.Cursor.Loc)
h.Relocate()
return true
}
// SelectToStartOfLine selects to the start of the current line
func (h *BufPane) SelectToStartOfLine() bool {
if !h.Cursor.HasSelection() {
@@ -396,6 +424,7 @@ func (h *BufPane) CursorStart() bool {
h.Cursor.Deselect(true)
h.Cursor.X = 0
h.Cursor.Y = 0
h.Cursor.StoreVisualX()
h.Relocate()
return true
}
@@ -456,7 +485,7 @@ func (h *BufPane) InsertNewline() bool {
// Remove the whitespaces if keepautoindent setting is off
if util.IsSpacesOrTabs(h.Buf.LineBytes(h.Cursor.Y-1)) && !h.Buf.Settings["keepautoindent"].(bool) {
line := h.Buf.LineBytes(h.Cursor.Y - 1)
h.Buf.Remove(buffer.Loc{X: 0, Y: h.Cursor.Y - 1}, buffer.Loc{X: utf8.RuneCount(line), Y: h.Cursor.Y - 1})
h.Buf.Remove(buffer.Loc{X: 0, Y: h.Cursor.Y - 1}, buffer.Loc{X: util.CharacterCount(line), Y: h.Cursor.Y - 1})
}
}
h.Cursor.LastVisualX = h.Cursor.GetVisualX()
@@ -481,7 +510,7 @@ func (h *BufPane) Backspace() bool {
// tab (tabSize number of spaces)
lineStart := util.SliceStart(h.Buf.LineBytes(h.Cursor.Y), h.Cursor.X)
tabSize := int(h.Buf.Settings["tabsize"].(float64))
if h.Buf.Settings["tabstospaces"].(bool) && util.IsSpaces(lineStart) && len(lineStart) != 0 && utf8.RuneCount(lineStart)%tabSize == 0 {
if h.Buf.Settings["tabstospaces"].(bool) && util.IsSpaces(lineStart) && len(lineStart) != 0 && util.CharacterCount(lineStart)%tabSize == 0 {
loc := h.Cursor.Loc
h.Buf.Remove(loc.Move(-tabSize, h.Buf), loc)
} else {
@@ -566,6 +595,20 @@ func (h *BufPane) IndentSelection() bool {
return false
}
// IndentLine moves the current line forward one indentation
func (h *BufPane) IndentLine() bool {
if h.Cursor.HasSelection() {
return false
}
tabsize := int(h.Buf.Settings["tabsize"].(float64))
indentstr := h.Buf.IndentString(tabsize)
h.Buf.Insert(buffer.Loc{X: 0, Y: h.Cursor.Y}, indentstr)
h.Buf.RelocateCursors()
h.Relocate()
return true
}
// OutdentLine moves the current line back one indentation
func (h *BufPane) OutdentLine() bool {
if h.Cursor.HasSelection() {
@@ -620,6 +663,11 @@ func (h *BufPane) Autocomplete() bool {
return false
}
if !util.IsNonAlphaNumeric(h.Cursor.RuneUnder(h.Cursor.X)) {
// don't autocomplete if cursor is on alpha numeric character (middle of a word)
return false
}
if b.HasSuggestions {
b.CycleAutocomplete(true)
return true
@@ -659,23 +707,27 @@ func (h *BufPane) SaveAll() bool {
return true
}
// Save the buffer to disk
func (h *BufPane) Save() bool {
// SaveCB performs a save and does a callback at the very end (after all prompts have been resolved)
func (h *BufPane) SaveCB(action string, callback func()) bool {
// If this is an empty buffer, ask for a filename
if h.Buf.Path == "" {
h.SaveAs()
h.SaveAsCB(action, callback)
} else {
noPrompt := h.saveBufToFile(h.Buf.Path, "Save")
noPrompt := h.saveBufToFile(h.Buf.Path, action, callback)
if noPrompt {
return true
}
}
return false
}
// SaveAs saves the buffer to disk with the given name
func (h *BufPane) SaveAs() bool {
// Save the buffer to disk
func (h *BufPane) Save() bool {
return h.SaveCB("Save", nil)
}
// SaveAsCB performs a save as and does a callback at the very end (after all prompts have been resolved)
func (h *BufPane) SaveAsCB(action string, callback func()) bool {
InfoBar.Prompt("Filename: ", "", "Save", nil, func(resp string, canceled bool) {
if !canceled {
// the filename might or might not be quoted, so unquote first then join the strings.
@@ -689,35 +741,50 @@ func (h *BufPane) SaveAs() bool {
return
}
filename := strings.Join(args, " ")
noPrompt := h.saveBufToFile(filename, "SaveAs")
noPrompt := h.saveBufToFile(filename, action, callback)
if noPrompt {
h.completeAction("SaveAs")
h.completeAction(action)
}
}
})
return false
}
// SaveAs saves the buffer to disk with the given name
func (h *BufPane) SaveAs() bool {
return h.SaveAsCB("SaveAs", nil)
}
// This function saves the buffer to `filename` and changes the buffer's path and name
// to `filename` if the save is successful
func (h *BufPane) saveBufToFile(filename string, action string) bool {
func (h *BufPane) saveBufToFile(filename string, action string, callback func()) bool {
err := h.Buf.SaveAs(filename)
if err != nil {
if strings.HasSuffix(err.Error(), "permission denied") {
InfoBar.YNPrompt("Permission denied. Do you want to save this file using sudo? (y,n)", func(yes, canceled bool) {
if yes && !canceled {
err = h.Buf.SaveAsWithSudo(filename)
if err != nil {
InfoBar.Error(err)
} else {
h.Buf.Path = filename
h.Buf.SetName(filename)
InfoBar.Message("Saved " + filename)
}
h.completeAction(action)
saveWithSudo := func() {
err = h.Buf.SaveAsWithSudo(filename)
if err != nil {
InfoBar.Error(err)
} else {
h.Buf.Path = filename
h.Buf.SetName(filename)
InfoBar.Message("Saved " + filename)
}
})
return false
}
if h.Buf.Settings["autosu"].(bool) {
saveWithSudo()
} else {
InfoBar.YNPrompt("Permission denied. Do you want to save this file using sudo? (y,n)", func(yes, canceled bool) {
if yes && !canceled {
saveWithSudo()
h.completeAction(action)
}
if callback != nil {
callback()
}
})
return false
}
} else {
InfoBar.Error(err)
}
@@ -726,15 +793,31 @@ func (h *BufPane) saveBufToFile(filename string, action string) bool {
h.Buf.SetName(filename)
InfoBar.Message("Saved " + filename)
}
if callback != nil {
callback()
}
return true
}
// Find opens a prompt and searches forward for the input
func (h *BufPane) Find() bool {
return h.find(true)
}
// FindLiteral is the same as Find() but does not support regular expressions
func (h *BufPane) FindLiteral() bool {
return h.find(false)
}
func (h *BufPane) find(useRegex bool) bool {
h.searchOrig = h.Cursor.Loc
InfoBar.Prompt("Find: ", "", "Find", func(resp string) {
prompt := "Find: "
if useRegex {
prompt = "Find (regex): "
}
InfoBar.Prompt(prompt, "", "Find", func(resp string) {
// Event callback
match, found, _ := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, true)
match, found, _ := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex)
if found {
h.Cursor.SetSelectionStart(match[0])
h.Cursor.SetSelectionEnd(match[1])
@@ -749,7 +832,7 @@ func (h *BufPane) Find() bool {
}, func(resp string, canceled bool) {
// Finished callback
if !canceled {
match, found, err := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, true)
match, found, err := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex)
if err != nil {
InfoBar.Error(err)
}
@@ -760,6 +843,7 @@ func (h *BufPane) Find() bool {
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
h.Cursor.GotoLoc(h.Cursor.CurSelection[1])
h.lastSearch = resp
h.lastSearchRegex = useRegex
} else {
h.Cursor.ResetSelection()
InfoBar.Message("No matches found")
@@ -783,7 +867,7 @@ func (h *BufPane) FindNext() bool {
if h.Cursor.HasSelection() {
searchLoc = h.Cursor.CurSelection[1]
}
match, found, err := h.Buf.FindNext(h.lastSearch, h.Buf.Start(), h.Buf.End(), searchLoc, true, true)
match, found, err := h.Buf.FindNext(h.lastSearch, h.Buf.Start(), h.Buf.End(), searchLoc, true, h.lastSearchRegex)
if err != nil {
InfoBar.Error(err)
}
@@ -810,7 +894,7 @@ func (h *BufPane) FindPrevious() bool {
if h.Cursor.HasSelection() {
searchLoc = h.Cursor.CurSelection[0]
}
match, found, err := h.Buf.FindNext(h.lastSearch, h.Buf.Start(), h.Buf.End(), searchLoc, false, true)
match, found, err := h.Buf.FindNext(h.lastSearch, h.Buf.Start(), h.Buf.End(), searchLoc, false, h.lastSearchRegex)
if err != nil {
InfoBar.Error(err)
}
@@ -858,6 +942,25 @@ func (h *BufPane) Copy() bool {
return true
}
// Copy the current line to the clipboard
func (h *BufPane) CopyLine() bool {
if h.Cursor.HasSelection() {
return false
} else {
h.Cursor.SelectLine()
h.Cursor.CopySelection("clipboard")
h.freshClip = true
if clipboard.Unsupported {
InfoBar.Message("Copied line (install xclip for external clipboard)")
} else {
InfoBar.Message("Copied line")
}
}
h.Cursor.Deselect(true)
h.Relocate()
return true
}
// CutLine cuts the current line to the clipboard
func (h *BufPane) CutLine() bool {
h.Cursor.SelectLine()
@@ -937,15 +1040,26 @@ func (h *BufPane) MoveLinesUp() bool {
}
start := h.Cursor.CurSelection[0].Y
end := h.Cursor.CurSelection[1].Y
sel := 1
if start > end {
end, start = start, end
sel = 0
}
compensate := false
if h.Cursor.CurSelection[sel].X != 0 {
end++
} else {
compensate = true
}
h.Buf.MoveLinesUp(
start,
end,
)
h.Cursor.CurSelection[1].Y -= 1
if compensate {
h.Cursor.CurSelection[sel].Y -= 1
}
} else {
if h.Cursor.Loc.Y == 0 {
InfoBar.Message("Cannot move further up")
@@ -970,8 +1084,14 @@ func (h *BufPane) MoveLinesDown() bool {
}
start := h.Cursor.CurSelection[0].Y
end := h.Cursor.CurSelection[1].Y
sel := 1
if start > end {
end, start = start, end
sel = 0
}
if h.Cursor.CurSelection[sel].X != 0 {
end++
}
h.Buf.MoveLinesDown(
@@ -1040,11 +1160,15 @@ func (h *BufPane) JumpToMatchingBrace() bool {
r := h.Cursor.RuneUnder(h.Cursor.X)
rl := h.Cursor.RuneUnder(h.Cursor.X - 1)
if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
matchingBrace, left := h.Buf.FindMatchingBrace(bp, h.Cursor.Loc)
if left {
h.Cursor.GotoLoc(matchingBrace)
matchingBrace, left, found := h.Buf.FindMatchingBrace(bp, h.Cursor.Loc)
if found {
if left {
h.Cursor.GotoLoc(matchingBrace)
} else {
h.Cursor.GotoLoc(matchingBrace.Move(1, h.Buf))
}
} else {
h.Cursor.GotoLoc(matchingBrace.Move(1, h.Buf))
return false
}
}
}
@@ -1074,6 +1198,16 @@ func (h *BufPane) OpenFile() bool {
return true
}
// OpenFile opens a new file in the buffer
func (h *BufPane) JumpLine() bool {
InfoBar.Prompt("> ", "goto ", "Command", nil, func(resp string, canceled bool) {
if !canceled {
h.HandleCommand(resp)
}
})
return true
}
// Start moves the viewport to the start of the buffer
func (h *BufPane) Start() bool {
v := h.GetView()
@@ -1195,6 +1329,21 @@ func (h *BufPane) HalfPageDown() bool {
return true
}
// ToggleDiffGutter turns the diff gutter off and on
func (h *BufPane) ToggleDiffGutter() bool {
if !h.Buf.Settings["diffgutter"].(bool) {
h.Buf.Settings["diffgutter"] = true
h.Buf.UpdateDiff(func(synchronous bool) {
screen.Redraw()
})
InfoBar.Message("Enabled diff gutter")
} else {
h.Buf.Settings["diffgutter"] = false
InfoBar.Message("Disabled diff gutter")
}
return true
}
// ToggleRuler turns line numbers off and on
func (h *BufPane) ToggleRuler() bool {
if !h.Buf.Settings["ruler"].(bool) {
@@ -1280,15 +1429,17 @@ func (h *BufPane) Quit() bool {
if h.Buf.Modified() {
if config.GlobalSettings["autosave"].(float64) > 0 {
// autosave on means we automatically save when quitting
h.Save()
quit()
h.SaveCB("Quit", func() {
quit()
})
} else {
InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) {
if !canceled && !yes {
quit()
} else if !canceled && yes {
h.Save()
quit()
h.SaveCB("Quit", func() {
quit()
})
}
})
}
@@ -1344,8 +1495,9 @@ func (h *BufPane) AddTab() bool {
// PreviousTab switches to the previous tab in the tab list
func (h *BufPane) PreviousTab() bool {
a := Tabs.Active()
Tabs.SetActive(util.Clamp(a-1, 0, len(Tabs.List)-1))
tabsLen := len(Tabs.List)
a := Tabs.Active() + tabsLen
Tabs.SetActive((a - 1) % tabsLen)
return true
}
@@ -1353,7 +1505,8 @@ func (h *BufPane) PreviousTab() bool {
// NextTab switches to the next tab in the tab list
func (h *BufPane) NextTab() bool {
a := Tabs.Active()
Tabs.SetActive(util.Clamp(a+1, 0, len(Tabs.List)-1))
Tabs.SetActive((a + 1) % len(Tabs.List))
return true
}
@@ -1505,7 +1658,7 @@ func (h *BufPane) SpawnMultiCursorUp() bool {
return true
}
// SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y more.
// SpawnMultiCursorDown creates additional cursor, at the same X (if possible), one Y more.
func (h *BufPane) SpawnMultiCursorDown() bool {
if h.Cursor.Y+1 == h.Buf.LinesNum() {
return false

View File

@@ -5,7 +5,7 @@ package action
import (
"syscall"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/v2/internal/screen"
)
// Suspend sends micro to the background. This is the same as pressing CtrlZ in most unix programs.

View File

@@ -5,22 +5,32 @@ import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
"unicode"
"github.com/zyedidia/json5"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/tcell"
)
func createBindingsIfNotExist(fname string) {
if _, e := os.Stat(fname); os.IsNotExist(e) {
ioutil.WriteFile(fname, []byte("{}"), 0644)
}
}
// InitBindings intializes the bindings map by reading from bindings.json
func InitBindings() {
config.Bindings = DefaultBindings()
var parsed map[string]string
defaults := DefaultBindings()
filename := config.ConfigDir + "/bindings.json"
filename := filepath.Join(config.ConfigDir, "bindings.json")
createBindingsIfNotExist(filename)
if _, e := os.Stat(filename); e == nil {
input, err := ioutil.ReadFile(filename)
if err != nil {
@@ -158,7 +168,8 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {
var e error
var parsed map[string]string
filename := config.ConfigDir + "/bindings.json"
filename := filepath.Join(config.ConfigDir, "bindings.json")
createBindingsIfNotExist(filename)
if _, e = os.Stat(filename); e == nil {
input, err := ioutil.ReadFile(filename)
if err != nil {
@@ -207,7 +218,8 @@ func UnbindKey(k string) error {
var e error
var parsed map[string]string
filename := config.ConfigDir + "/bindings.json"
filename := filepath.Join(config.ConfigDir, "bindings.json")
createBindingsIfNotExist(filename)
if _, e = os.Stat(filename); e == nil {
input, err := ioutil.ReadFile(filename)
if err != nil {
@@ -237,6 +249,7 @@ func UnbindKey(k string) error {
if a, ok := defaults[k]; ok {
BindKey(k, a)
} else if _, ok := config.Bindings[k]; ok {
BufUnmap(key)
delete(config.Bindings, k)
}

View File

@@ -7,11 +7,11 @@ import (
luar "layeh.com/gopher-luar"
lua "github.com/yuin/gopher-lua"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/display"
ulua "github.com/zyedidia/micro/internal/lua"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/display"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/tcell"
)
@@ -103,7 +103,7 @@ func BufMapKey(k Event, action string) {
afn = f
names = append(names, a)
} else {
screen.TermMessage("Error:", a, "does not exist")
screen.TermMessage("Error in bindings: action", a, "does not exist")
continue
}
actionfns = append(actionfns, afn)
@@ -113,6 +113,9 @@ func BufMapKey(k Event, action string) {
success := true
for i, a := range actionfns {
for j, c := range cursors {
if c == nil {
continue
}
h.Buf.SetCurCursor(c.Num)
h.Cursor = c
if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
@@ -136,6 +139,17 @@ func BufMapMouse(k MouseEvent, action string) {
}
}
// BufUnmap unmaps a key or mouse event from any action
func BufUnmap(k Event) {
delete(BufKeyBindings, k)
delete(BufKeyStrings, k)
switch e := k.(type) {
case MouseEvent:
delete(BufMouseBindings, e)
}
}
// The BufPane connects the buffer and the window
// It provides a cursor (or multiple) and defines a set of actions
// that can be taken on the buffer
@@ -176,7 +190,8 @@ type BufPane struct {
tripleClick bool
// Last search stores the last successful search for FindNext and FindPrev
lastSearch string
lastSearch string
lastSearchRegex bool
// Should the current multiple cursor selection search based on word or
// based on selection (false for selection, true for word)
multiWord bool
@@ -267,13 +282,20 @@ func (h *BufPane) SetID(i uint64) {
}
func (h *BufPane) Name() string {
return h.Buf.GetName()
n := h.Buf.GetName()
if h.Buf.Modified() {
n += " +"
}
return n
}
// HandleEvent executes the tcell event properly
func (h *BufPane) HandleEvent(event tcell.Event) {
if h.Buf.ExternallyModified() {
InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n)", func(yes, canceled bool) {
if h.Buf.ExternallyModified() && !h.Buf.ReloadDisabled {
InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) {
if canceled {
h.Buf.DisableReload()
}
if !yes || canceled {
h.Buf.UpdateModTime()
} else {
@@ -333,10 +355,11 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
// release the mouse
// if !h.doubleClick && !h.tripleClick {
// h.Cursor.Loc = mouseLoc
// h.Cursor.SetSelectionEnd(h.Cursor.Loc)
// h.Cursor.CopySelection("primary")
// }
if h.Cursor.HasSelection() {
h.Cursor.CopySelection("primary")
}
h.mouseReleased = true
}
}
@@ -452,6 +475,7 @@ func (h *BufPane) DoRuneInsert(r rune) {
if recording_macro {
curmacro = append(curmacro, r)
}
h.Relocate()
h.PluginCBRune("onRune", r)
}
}
@@ -505,104 +529,111 @@ func (h *BufPane) SetActive(b bool) {
// BufKeyActions contains the list of all possible key actions the bufhandler could execute
var BufKeyActions = map[string]BufKeyAction{
"CursorUp": (*BufPane).CursorUp,
"CursorDown": (*BufPane).CursorDown,
"CursorPageUp": (*BufPane).CursorPageUp,
"CursorPageDown": (*BufPane).CursorPageDown,
"CursorLeft": (*BufPane).CursorLeft,
"CursorRight": (*BufPane).CursorRight,
"CursorStart": (*BufPane).CursorStart,
"CursorEnd": (*BufPane).CursorEnd,
"SelectToStart": (*BufPane).SelectToStart,
"SelectToEnd": (*BufPane).SelectToEnd,
"SelectUp": (*BufPane).SelectUp,
"SelectDown": (*BufPane).SelectDown,
"SelectLeft": (*BufPane).SelectLeft,
"SelectRight": (*BufPane).SelectRight,
"WordRight": (*BufPane).WordRight,
"WordLeft": (*BufPane).WordLeft,
"SelectWordRight": (*BufPane).SelectWordRight,
"SelectWordLeft": (*BufPane).SelectWordLeft,
"DeleteWordRight": (*BufPane).DeleteWordRight,
"DeleteWordLeft": (*BufPane).DeleteWordLeft,
"SelectLine": (*BufPane).SelectLine,
"SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
"SelectToStartOfText": (*BufPane).SelectToStartOfText,
"SelectToEndOfLine": (*BufPane).SelectToEndOfLine,
"ParagraphPrevious": (*BufPane).ParagraphPrevious,
"ParagraphNext": (*BufPane).ParagraphNext,
"InsertNewline": (*BufPane).InsertNewline,
"Backspace": (*BufPane).Backspace,
"Delete": (*BufPane).Delete,
"InsertTab": (*BufPane).InsertTab,
"Save": (*BufPane).Save,
"SaveAll": (*BufPane).SaveAll,
"SaveAs": (*BufPane).SaveAs,
"Find": (*BufPane).Find,
"FindNext": (*BufPane).FindNext,
"FindPrevious": (*BufPane).FindPrevious,
"Center": (*BufPane).Center,
"Undo": (*BufPane).Undo,
"Redo": (*BufPane).Redo,
"Copy": (*BufPane).Copy,
"Cut": (*BufPane).Cut,
"CutLine": (*BufPane).CutLine,
"DuplicateLine": (*BufPane).DuplicateLine,
"DeleteLine": (*BufPane).DeleteLine,
"MoveLinesUp": (*BufPane).MoveLinesUp,
"MoveLinesDown": (*BufPane).MoveLinesDown,
"IndentSelection": (*BufPane).IndentSelection,
"OutdentSelection": (*BufPane).OutdentSelection,
"Autocomplete": (*BufPane).Autocomplete,
"CycleAutocompleteBack": (*BufPane).CycleAutocompleteBack,
"OutdentLine": (*BufPane).OutdentLine,
"Paste": (*BufPane).Paste,
"PastePrimary": (*BufPane).PastePrimary,
"SelectAll": (*BufPane).SelectAll,
"OpenFile": (*BufPane).OpenFile,
"Start": (*BufPane).Start,
"End": (*BufPane).End,
"PageUp": (*BufPane).PageUp,
"PageDown": (*BufPane).PageDown,
"SelectPageUp": (*BufPane).SelectPageUp,
"SelectPageDown": (*BufPane).SelectPageDown,
"HalfPageUp": (*BufPane).HalfPageUp,
"HalfPageDown": (*BufPane).HalfPageDown,
"StartOfText": (*BufPane).StartOfText,
"StartOfLine": (*BufPane).StartOfLine,
"EndOfLine": (*BufPane).EndOfLine,
"ToggleHelp": (*BufPane).ToggleHelp,
"ToggleKeyMenu": (*BufPane).ToggleKeyMenu,
"ToggleRuler": (*BufPane).ToggleRuler,
"ClearStatus": (*BufPane).ClearStatus,
"ShellMode": (*BufPane).ShellMode,
"CommandMode": (*BufPane).CommandMode,
"ToggleOverwriteMode": (*BufPane).ToggleOverwriteMode,
"Escape": (*BufPane).Escape,
"Quit": (*BufPane).Quit,
"QuitAll": (*BufPane).QuitAll,
"AddTab": (*BufPane).AddTab,
"PreviousTab": (*BufPane).PreviousTab,
"NextTab": (*BufPane).NextTab,
"NextSplit": (*BufPane).NextSplit,
"PreviousSplit": (*BufPane).PreviousSplit,
"Unsplit": (*BufPane).Unsplit,
"VSplit": (*BufPane).VSplitAction,
"HSplit": (*BufPane).HSplitAction,
"ToggleMacro": (*BufPane).ToggleMacro,
"PlayMacro": (*BufPane).PlayMacro,
"Suspend": (*BufPane).Suspend,
"ScrollUp": (*BufPane).ScrollUpAction,
"ScrollDown": (*BufPane).ScrollDownAction,
"SpawnMultiCursor": (*BufPane).SpawnMultiCursor,
"SpawnMultiCursorUp": (*BufPane).SpawnMultiCursorUp,
"SpawnMultiCursorDown": (*BufPane).SpawnMultiCursorDown,
"SpawnMultiCursorSelect": (*BufPane).SpawnMultiCursorSelect,
"RemoveMultiCursor": (*BufPane).RemoveMultiCursor,
"RemoveAllMultiCursors": (*BufPane).RemoveAllMultiCursors,
"SkipMultiCursor": (*BufPane).SkipMultiCursor,
"JumpToMatchingBrace": (*BufPane).JumpToMatchingBrace,
"None": (*BufPane).None,
"CursorUp": (*BufPane).CursorUp,
"CursorDown": (*BufPane).CursorDown,
"CursorPageUp": (*BufPane).CursorPageUp,
"CursorPageDown": (*BufPane).CursorPageDown,
"CursorLeft": (*BufPane).CursorLeft,
"CursorRight": (*BufPane).CursorRight,
"CursorStart": (*BufPane).CursorStart,
"CursorEnd": (*BufPane).CursorEnd,
"SelectToStart": (*BufPane).SelectToStart,
"SelectToEnd": (*BufPane).SelectToEnd,
"SelectUp": (*BufPane).SelectUp,
"SelectDown": (*BufPane).SelectDown,
"SelectLeft": (*BufPane).SelectLeft,
"SelectRight": (*BufPane).SelectRight,
"WordRight": (*BufPane).WordRight,
"WordLeft": (*BufPane).WordLeft,
"SelectWordRight": (*BufPane).SelectWordRight,
"SelectWordLeft": (*BufPane).SelectWordLeft,
"DeleteWordRight": (*BufPane).DeleteWordRight,
"DeleteWordLeft": (*BufPane).DeleteWordLeft,
"SelectLine": (*BufPane).SelectLine,
"SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
"SelectToStartOfText": (*BufPane).SelectToStartOfText,
"SelectToStartOfTextToggle": (*BufPane).SelectToStartOfTextToggle,
"SelectToEndOfLine": (*BufPane).SelectToEndOfLine,
"ParagraphPrevious": (*BufPane).ParagraphPrevious,
"ParagraphNext": (*BufPane).ParagraphNext,
"InsertNewline": (*BufPane).InsertNewline,
"Backspace": (*BufPane).Backspace,
"Delete": (*BufPane).Delete,
"InsertTab": (*BufPane).InsertTab,
"Save": (*BufPane).Save,
"SaveAll": (*BufPane).SaveAll,
"SaveAs": (*BufPane).SaveAs,
"Find": (*BufPane).Find,
"FindLiteral": (*BufPane).FindLiteral,
"FindNext": (*BufPane).FindNext,
"FindPrevious": (*BufPane).FindPrevious,
"Center": (*BufPane).Center,
"Undo": (*BufPane).Undo,
"Redo": (*BufPane).Redo,
"Copy": (*BufPane).Copy,
"CopyLine": (*BufPane).CopyLine,
"Cut": (*BufPane).Cut,
"CutLine": (*BufPane).CutLine,
"DuplicateLine": (*BufPane).DuplicateLine,
"DeleteLine": (*BufPane).DeleteLine,
"MoveLinesUp": (*BufPane).MoveLinesUp,
"MoveLinesDown": (*BufPane).MoveLinesDown,
"IndentSelection": (*BufPane).IndentSelection,
"OutdentSelection": (*BufPane).OutdentSelection,
"Autocomplete": (*BufPane).Autocomplete,
"CycleAutocompleteBack": (*BufPane).CycleAutocompleteBack,
"OutdentLine": (*BufPane).OutdentLine,
"IndentLine": (*BufPane).IndentLine,
"Paste": (*BufPane).Paste,
"PastePrimary": (*BufPane).PastePrimary,
"SelectAll": (*BufPane).SelectAll,
"OpenFile": (*BufPane).OpenFile,
"Start": (*BufPane).Start,
"End": (*BufPane).End,
"PageUp": (*BufPane).PageUp,
"PageDown": (*BufPane).PageDown,
"SelectPageUp": (*BufPane).SelectPageUp,
"SelectPageDown": (*BufPane).SelectPageDown,
"HalfPageUp": (*BufPane).HalfPageUp,
"HalfPageDown": (*BufPane).HalfPageDown,
"StartOfText": (*BufPane).StartOfText,
"StartOfTextToggle": (*BufPane).StartOfTextToggle,
"StartOfLine": (*BufPane).StartOfLine,
"EndOfLine": (*BufPane).EndOfLine,
"ToggleHelp": (*BufPane).ToggleHelp,
"ToggleKeyMenu": (*BufPane).ToggleKeyMenu,
"ToggleDiffGutter": (*BufPane).ToggleDiffGutter,
"ToggleRuler": (*BufPane).ToggleRuler,
"ClearStatus": (*BufPane).ClearStatus,
"ShellMode": (*BufPane).ShellMode,
"CommandMode": (*BufPane).CommandMode,
"ToggleOverwriteMode": (*BufPane).ToggleOverwriteMode,
"Escape": (*BufPane).Escape,
"Quit": (*BufPane).Quit,
"QuitAll": (*BufPane).QuitAll,
"AddTab": (*BufPane).AddTab,
"PreviousTab": (*BufPane).PreviousTab,
"NextTab": (*BufPane).NextTab,
"NextSplit": (*BufPane).NextSplit,
"PreviousSplit": (*BufPane).PreviousSplit,
"Unsplit": (*BufPane).Unsplit,
"VSplit": (*BufPane).VSplitAction,
"HSplit": (*BufPane).HSplitAction,
"ToggleMacro": (*BufPane).ToggleMacro,
"PlayMacro": (*BufPane).PlayMacro,
"Suspend": (*BufPane).Suspend,
"ScrollUp": (*BufPane).ScrollUpAction,
"ScrollDown": (*BufPane).ScrollDownAction,
"SpawnMultiCursor": (*BufPane).SpawnMultiCursor,
"SpawnMultiCursorUp": (*BufPane).SpawnMultiCursorUp,
"SpawnMultiCursorDown": (*BufPane).SpawnMultiCursorDown,
"SpawnMultiCursorSelect": (*BufPane).SpawnMultiCursorSelect,
"RemoveMultiCursor": (*BufPane).RemoveMultiCursor,
"RemoveAllMultiCursors": (*BufPane).RemoveAllMultiCursors,
"SkipMultiCursor": (*BufPane).SkipMultiCursor,
"JumpToMatchingBrace": (*BufPane).JumpToMatchingBrace,
"JumpLine": (*BufPane).JumpLine,
"None": (*BufPane).None,
// This was changed to InsertNewline but I don't want to break backwards compatibility
"InsertEnter": (*BufPane).InsertNewline,
@@ -619,53 +650,57 @@ var BufMouseActions = map[string]BufMouseAction{
// Generally actions that modify global editor state like quitting or
// saving should not be included in this list
var MultiActions = map[string]bool{
"CursorUp": true,
"CursorDown": true,
"CursorPageUp": true,
"CursorPageDown": true,
"CursorLeft": true,
"CursorRight": true,
"CursorStart": true,
"CursorEnd": true,
"SelectToStart": true,
"SelectToEnd": true,
"SelectUp": true,
"SelectDown": true,
"SelectLeft": true,
"SelectRight": true,
"WordRight": true,
"WordLeft": true,
"SelectWordRight": true,
"SelectWordLeft": true,
"DeleteWordRight": true,
"DeleteWordLeft": true,
"SelectLine": true,
"SelectToStartOfLine": true,
"SelectToStartOfText": true,
"SelectToEndOfLine": true,
"ParagraphPrevious": true,
"ParagraphNext": true,
"InsertNewline": true,
"Backspace": true,
"Delete": true,
"InsertTab": true,
"FindNext": true,
"FindPrevious": true,
"Cut": true,
"CutLine": true,
"DuplicateLine": true,
"DeleteLine": true,
"MoveLinesUp": true,
"MoveLinesDown": true,
"IndentSelection": true,
"OutdentSelection": true,
"OutdentLine": true,
"Paste": true,
"PastePrimary": true,
"SelectPageUp": true,
"SelectPageDown": true,
"StartOfLine": true,
"StartOfText": true,
"EndOfLine": true,
"JumpToMatchingBrace": true,
"CursorUp": true,
"CursorDown": true,
"CursorPageUp": true,
"CursorPageDown": true,
"CursorLeft": true,
"CursorRight": true,
"CursorStart": true,
"CursorEnd": true,
"SelectToStart": true,
"SelectToEnd": true,
"SelectUp": true,
"SelectDown": true,
"SelectLeft": true,
"SelectRight": true,
"WordRight": true,
"WordLeft": true,
"SelectWordRight": true,
"SelectWordLeft": true,
"DeleteWordRight": true,
"DeleteWordLeft": true,
"SelectLine": true,
"SelectToStartOfLine": true,
"SelectToStartOfText": true,
"SelectToStartOfTextToggle": true,
"SelectToEndOfLine": true,
"ParagraphPrevious": true,
"ParagraphNext": true,
"InsertNewline": true,
"Backspace": true,
"Delete": true,
"InsertTab": true,
"FindNext": true,
"FindPrevious": true,
"CopyLine": true,
"Cut": true,
"CutLine": true,
"DuplicateLine": true,
"DeleteLine": true,
"MoveLinesUp": true,
"MoveLinesDown": true,
"IndentSelection": true,
"OutdentSelection": true,
"OutdentLine": true,
"IndentLine": true,
"Paste": true,
"PastePrimary": true,
"SelectPageUp": true,
"SelectPageDown": true,
"StartOfLine": true,
"StartOfText": true,
"StartOfTextToggle": true,
"EndOfLine": true,
"JumpToMatchingBrace": true,
}

View File

@@ -10,14 +10,13 @@ import (
"regexp"
"strconv"
"strings"
"unicode/utf8"
shellquote "github.com/kballard/go-shellquote"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/shell"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
)
// A Command contains information about how to execute a command
@@ -56,6 +55,7 @@ func InitCommands() {
"cd": {(*BufPane).CdCmd, buffer.FileComplete},
"pwd": {(*BufPane).PwdCmd, nil},
"open": {(*BufPane).OpenCmd, buffer.FileComplete},
"tabmove": {(*BufPane).TabMoveCmd, nil},
"tabswitch": {(*BufPane).TabSwitchCmd, nil},
"term": {(*BufPane).TermCmd, nil},
"memusage": {(*BufPane).MemUsageCmd, nil},
@@ -106,7 +106,7 @@ func (h *BufPane) PluginCmd(args []string) {
}
if h.Buf.Type != buffer.BTLog {
OpenLogBuf(h)
h.OpenLogBuf()
}
config.PluginCommand(buffer.LogBuf, args[0], args[1:])
@@ -155,6 +155,56 @@ func (h *BufPane) TextFilterCmd(args []string) {
h.Buf.Insert(h.Cursor.Loc, bout.String())
}
// TabMoveCmd moves the current tab to a given index (starts at 1). The
// displaced tabs are moved up.
func (h *BufPane) TabMoveCmd(args []string) {
if len(args) <= 0 {
InfoBar.Error("Not enough arguments: provide an index, starting at 1")
return
}
if len(args[0]) <= 0 {
InfoBar.Error("Invalid argument: empty string")
return
}
num, err := strconv.Atoi(args[0])
if err != nil {
InfoBar.Error("Invalid argument: ", err)
return
}
// Preserve sign for relative move, if one exists
var shiftDirection byte
if strings.Contains("-+", string([]byte{args[0][0]})) {
shiftDirection = args[0][0]
}
// Relative positions -> absolute positions
idxFrom := Tabs.Active()
idxTo := 0
offset := util.Abs(num)
if shiftDirection == '-' {
idxTo = idxFrom - offset
} else if shiftDirection == '+' {
idxTo = idxFrom + offset
} else {
idxTo = offset - 1
}
// Restrain position to within the valid range
idxTo = util.Clamp(idxTo, 0, len(Tabs.List)-1)
activeTab := Tabs.List[idxFrom]
Tabs.RemoveTab(activeTab.ID())
Tabs.List = append(Tabs.List, nil)
copy(Tabs.List[idxTo+1:], Tabs.List[idxTo:])
Tabs.List[idxTo] = activeTab
Tabs.UpdateNames()
Tabs.SetActive(idxTo)
// InfoBar.Message(fmt.Sprintf("Moved tab from slot %d to %d", idxFrom+1, idxTo+1))
}
// TabSwitchCmd switches to a given tab either by name or by number
func (h *BufPane) TabSwitchCmd(args []string) {
if len(args) > 0 {
@@ -272,7 +322,7 @@ func (h *BufPane) OpenCmd(args []string) {
// ToggleLogCmd toggles the log view
func (h *BufPane) ToggleLogCmd(args []string) {
if h.Buf.Type != buffer.BTLog {
OpenLogBuf(h)
h.OpenLogBuf()
} else {
h.Quit()
}
@@ -475,7 +525,7 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error {
b.SetOptionNative(option, nativeValue)
}
return config.WriteSettings(config.ConfigDir + "/settings.json")
return config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json"))
}
func SetGlobalOption(option, value string) error {
@@ -652,7 +702,7 @@ func (h *BufPane) GotoCmd(args []string) {
return
}
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
col = util.Clamp(col-1, 0, utf8.RuneCount(h.Buf.LineBytes(line)))
col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
h.Cursor.GotoLoc(buffer.Loc{col, line})
} else {
line, err := strconv.Atoi(args[0])
@@ -732,23 +782,23 @@ func (h *BufPane) ReplaceCmd(args []string) {
nreplaced := 0
start := h.Buf.Start()
// end := h.Buf.End()
// if h.Cursor.HasSelection() {
// start = h.Cursor.CurSelection[0]
// end = h.Cursor.CurSelection[1]
// }
end := h.Buf.End()
selection := h.Cursor.HasSelection()
if selection {
start = h.Cursor.CurSelection[0]
end = h.Cursor.CurSelection[1]
}
if all {
nreplaced = h.Buf.ReplaceRegex(start, h.Buf.End(), regex, replace)
nreplaced, _ = h.Buf.ReplaceRegex(start, end, regex, replace)
} else {
inRange := func(l buffer.Loc) bool {
return l.GreaterEqual(start) && l.LessEqual(h.Buf.End())
return l.GreaterEqual(start) && l.LessEqual(end)
}
searchLoc := start
searching := true
var doReplacement func()
doReplacement = func() {
locs, found, err := h.Buf.FindNext(search, start, h.Buf.End(), searchLoc, true, !noRegex)
locs, found, err := h.Buf.FindNext(search, start, end, searchLoc, true, !noRegex)
if err != nil {
InfoBar.Error(err)
return
@@ -761,40 +811,50 @@ func (h *BufPane) ReplaceCmd(args []string) {
h.Cursor.SetSelectionStart(locs[0])
h.Cursor.SetSelectionEnd(locs[1])
h.Cursor.GotoLoc(locs[0])
h.Relocate()
InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) {
if !canceled && yes {
h.Buf.Replace(locs[0], locs[1], replaceStr)
_, nrunes := h.Buf.ReplaceRegex(locs[0], locs[1], regex, replace)
searchLoc = locs[0]
searchLoc.X += utf8.RuneCount(replace)
searchLoc.X += nrunes + locs[0].Diff(locs[1], h.Buf)
end.Move(nrunes, h.Buf)
h.Cursor.Loc = searchLoc
nreplaced++
} else if !canceled && !yes {
searchLoc = locs[0]
searchLoc.X += utf8.RuneCount(replace)
searchLoc.X += util.CharacterCount(replace)
} else if canceled {
h.Cursor.ResetSelection()
h.Buf.RelocateCursors()
return
}
if searching {
doReplacement()
}
doReplacement()
})
}
doReplacement()
}
h.Buf.RelocateCursors()
h.Relocate()
var s string
if nreplaced > 1 {
InfoBar.Message("Replaced ", nreplaced, " occurrences of ", search)
s = fmt.Sprintf("Replaced %d occurrences of %s", nreplaced, search)
} else if nreplaced == 1 {
InfoBar.Message("Replaced ", nreplaced, " occurrence of ", search)
s = fmt.Sprintf("Replaced 1 occurrence of %s", search)
} else {
InfoBar.Message("Nothing matched ", search)
s = fmt.Sprintf("Nothing matched %s", search)
}
if selection {
s += " in selection"
}
InfoBar.Message(s)
}
// ReplaceAllCmd replaces search term all at once
@@ -807,6 +867,11 @@ func (h *BufPane) ReplaceAllCmd(args []string) {
func (h *BufPane) TermCmd(args []string) {
ps := h.tab.Panes
if !TermEmuSupported {
InfoBar.Error("Terminal emulator not supported on this system")
return
}
if len(args) == 0 {
sh := os.Getenv("SHELL")
if sh == "" {
@@ -818,7 +883,11 @@ func (h *BufPane) TermCmd(args []string) {
term := func(i int, newtab bool) {
t := new(shell.Terminal)
t.Start(args, false, true, nil, nil)
err := t.Start(args, false, true, nil, nil)
if err != nil {
InfoBar.Error(err)
return
}
id := h.ID()
if newtab {
@@ -830,7 +899,12 @@ func (h *BufPane) TermCmd(args []string) {
}
v := h.GetView()
MainTab().Panes[i] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
tp, err := NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
if err != nil {
InfoBar.Error(err)
return
}
MainTab().Panes[i] = tp
MainTab().SetActive(i)
}

View File

@@ -17,10 +17,10 @@ func DefaultBindings() map[string]string {
"AltDown": "MoveLinesDown",
"AltShiftRight": "SelectWordRight",
"AltShiftLeft": "SelectWordLeft",
"CtrlLeft": "StartOfText",
"CtrlLeft": "StartOfTextToggle",
"CtrlRight": "EndOfLine",
"CtrlShiftLeft": "SelectToStartOfText",
"ShiftHome": "SelectToStartOfText",
"CtrlShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"CtrlShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
@@ -43,7 +43,7 @@ func DefaultBindings() map[string]string {
"CtrlP": "FindPrevious",
"CtrlZ": "Undo",
"CtrlY": "Redo",
"CtrlC": "Copy",
"CtrlC": "CopyLine|Copy",
"CtrlX": "Cut",
"CtrlK": "CutLine",
"CtrlD": "DuplicateLine",
@@ -52,7 +52,7 @@ func DefaultBindings() map[string]string {
"CtrlT": "AddTab",
"Alt,": "PreviousTab",
"Alt.": "NextTab",
"Home": "StartOfText",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",

View File

@@ -19,10 +19,10 @@ func DefaultBindings() map[string]string {
"AltDown": "MoveLinesDown",
"CtrlShiftRight": "SelectWordRight",
"CtrlShiftLeft": "SelectWordLeft",
"AltLeft": "StartOfText",
"AltLeft": "StartOfTextToggle",
"AltRight": "EndOfLine",
"AltShiftLeft": "SelectToStartOfText",
"ShiftHome": "SelectToStartOfText",
"AltShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"AltShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
@@ -45,7 +45,7 @@ func DefaultBindings() map[string]string {
"CtrlP": "FindPrevious",
"CtrlZ": "Undo",
"CtrlY": "Redo",
"CtrlC": "Copy",
"CtrlC": "CopyLine|Copy",
"CtrlX": "Cut",
"CtrlK": "CutLine",
"CtrlD": "DuplicateLine",
@@ -54,7 +54,7 @@ func DefaultBindings() map[string]string {
"CtrlT": "AddTab",
"Alt,": "PreviousTab",
"Alt.": "NextTab",
"Home": "StartOfText",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",

View File

@@ -1,19 +1,22 @@
package action
import "github.com/zyedidia/micro/internal/buffer"
import "github.com/zyedidia/micro/v2/internal/buffer"
var InfoBar *InfoPane
var LogBufPane *BufPane
// InitGlobals initializes the log buffer and the info bar
func InitGlobals() {
InfoBar = NewInfoBar()
buffer.LogBuf = buffer.NewBufferFromString("", "Log", buffer.BTLog)
}
// GetInfoBar returns the infobar pane
func GetInfoBar() *InfoPane {
return InfoBar
}
// WriteLog writes a string to the log buffer
func WriteLog(s string) {
buffer.WriteLog(s)
if LogBufPane != nil {
@@ -28,7 +31,10 @@ func WriteLog(s string) {
}
}
func OpenLogBuf(h *BufPane) {
// OpenLogBuf opens the log buffer from the current bufpane
// If the current bufpane is a log buffer nothing happens,
// otherwise the log buffer is opened in a horizontal split
func (h *BufPane) OpenLogBuf() {
LogBufPane = h.HSplitBuf(buffer.LogBuf)
LogBufPane.CursorEnd()

View File

@@ -5,9 +5,9 @@ import (
"sort"
"strings"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/util"
)
// This file is meant (for now) for autocompletion in command mode, not

View File

@@ -4,9 +4,10 @@ import (
"bytes"
"strings"
"github.com/zyedidia/micro/internal/display"
"github.com/zyedidia/micro/internal/info"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/micro/v2/internal/info"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
)
@@ -68,7 +69,7 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
h.EventCallback(resp)
}
}
case *tcell.EventMouse:
default:
h.BufPane.HandleEvent(event)
}
}
@@ -123,6 +124,7 @@ var InfoNones = []string{
"HalfPageDown",
"ToggleHelp",
"ToggleKeyMenu",
"ToggleDiffGutter",
"ToggleRuler",
"JumpLine",
"ClearStatus",
@@ -185,14 +187,17 @@ func (h *InfoPane) Autocomplete() {
args := bytes.Split(l, []byte{' '})
cmd := string(args[0])
if len(args) == 1 {
b.Autocomplete(CommandComplete)
} else {
if action, ok := commands[cmd]; ok {
if h.PromptType == "Command" {
if len(args) == 1 {
b.Autocomplete(CommandComplete)
} else if action, ok := commands[cmd]; ok {
if action.completer != nil {
b.Autocomplete(action.completer)
}
}
} else {
// by default use filename autocompletion
b.Autocomplete(buffer.FileComplete)
}
}

View File

@@ -1,7 +1,7 @@
package action
import (
"github.com/zyedidia/micro/internal/display"
"github.com/zyedidia/micro/v2/internal/display"
)
type Pane interface {

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"reflect"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/display"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/tcell"
)

View File

@@ -1,11 +1,11 @@
package action
import (
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/display"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/views"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/views"
"github.com/zyedidia/tcell"
)
@@ -104,6 +104,13 @@ func (t *TabList) HandleEvent(event tcell.Event) {
mx, my := e.Position()
switch e.Buttons() {
case tcell.Button1:
if my == t.Y && mx == 0 {
t.Scroll(-4)
return
} else if my == t.Y && mx == t.Width-1 {
t.Scroll(4)
return
}
if len(t.List) > 1 {
ind := t.LocFromVisual(buffer.Loc{mx, my})
if ind != -1 {
@@ -159,6 +166,8 @@ type Tab struct {
active int
resizing *views.Node // node currently being resized
// captures whether the mouse is released
release bool
}
// NewTabFromBuffer creates a new tab from the given buffer
@@ -196,6 +205,8 @@ func (t *Tab) HandleEvent(event tcell.Event) {
mx, my := e.Position()
switch e.Buttons() {
case tcell.Button1:
wasReleased := t.release
t.release = false
if t.resizing != nil {
var size int
if t.resizing.Kind == views.STVert {
@@ -208,22 +219,25 @@ func (t *Tab) HandleEvent(event tcell.Event) {
return
}
resizeID := t.GetMouseSplitID(buffer.Loc{mx, my})
if resizeID != 0 {
t.resizing = t.GetNode(uint64(resizeID))
return
}
if wasReleased {
resizeID := t.GetMouseSplitID(buffer.Loc{mx, my})
if resizeID != 0 {
t.resizing = t.GetNode(uint64(resizeID))
return
}
for i, p := range t.Panes {
v := p.GetView()
inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
if inpane {
t.SetActive(i)
break
for i, p := range t.Panes {
v := p.GetView()
inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
if inpane {
t.SetActive(i)
break
}
}
}
case tcell.ButtonNone:
t.resizing = nil
t.release = true
default:
for _, p := range t.Panes {
v := p.GetView()

View File

@@ -4,7 +4,7 @@ package action
import (
shellquote "github.com/kballard/go-shellquote"
"github.com/zyedidia/micro/internal/shell"
"github.com/zyedidia/micro/v2/internal/shell"
)
// TermEmuSupported is a constant that marks if the terminal emulator is supported
@@ -30,7 +30,12 @@ func RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, callba
id := MainTab().Panes[0].ID()
v := h.GetView()
MainTab().Panes[0] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
tp, err := NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
if err != nil {
return err
}
MainTab().Panes[0] = tp
MainTab().SetActive(0)
return nil

View File

@@ -1,12 +1,13 @@
package action
import (
"errors"
"runtime"
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/internal/display"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/shell"
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/tcell"
"github.com/zyedidia/terminal"
)
@@ -20,14 +21,18 @@ type TermPane struct {
tab *Tab
}
func NewTermPane(x, y, w, h int, t *shell.Terminal, id uint64, tab *Tab) *TermPane {
func NewTermPane(x, y, w, h int, t *shell.Terminal, id uint64, tab *Tab) (*TermPane, error) {
if !TermEmuSupported {
return nil, errors.New("Terminal emulator is not supported on this system")
}
th := new(TermPane)
th.Terminal = t
th.id = id
th.mouseReleased = true
th.Window = display.NewTermWindow(x, y, w, h, t)
th.tab = tab
return th
return th, nil
}
func (t *TermPane) ID() uint64 {

View File

@@ -6,9 +6,8 @@ import (
"os"
"sort"
"strings"
"unicode/utf8"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/util"
)
// A Completer is a function that takes a buffer and returns info
@@ -54,7 +53,7 @@ func (b *Buffer) CycleAutocomplete(forward bool) {
start := c.Loc
end := c.Loc
if prevSuggestion < len(b.Suggestions) && prevSuggestion >= 0 {
start = end.Move(-utf8.RuneCountInString(b.Completions[prevSuggestion]), b)
start = end.Move(-util.CharacterCountInString(b.Completions[prevSuggestion]), b)
} else {
// end = start.Move(1, b)
}
@@ -82,7 +81,7 @@ func GetWord(b *Buffer) ([]byte, int) {
args := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
input := args[len(args)-1]
return input, c.X - utf8.RuneCount(input)
return input, c.X - util.CharacterCount(input)
}
// GetArg gets the most recent word (separated by ' ' only)
@@ -98,7 +97,7 @@ func GetArg(b *Buffer) (string, int) {
if i == len(args)-1 {
break
}
argstart += utf8.RuneCount(a) + 1
argstart += util.CharacterCount(a) + 1
}
return input, argstart
@@ -162,7 +161,7 @@ func BufferComplete(b *Buffer) ([]string, []string) {
return []string{}, []string{}
}
inputLen := utf8.RuneCount(input)
inputLen := util.CharacterCount(input)
suggestionsSet := make(map[string]struct{})
@@ -171,7 +170,7 @@ func BufferComplete(b *Buffer) ([]string, []string) {
l := b.LineBytes(i)
words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
for _, w := range words {
if bytes.HasPrefix(w, input) && utf8.RuneCount(w) > inputLen {
if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
strw := string(w)
if _, ok := suggestionsSet[strw]; !ok {
suggestionsSet[strw] = struct{}{}
@@ -184,7 +183,7 @@ func BufferComplete(b *Buffer) ([]string, []string) {
l := b.LineBytes(i)
words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
for _, w := range words {
if bytes.HasPrefix(w, input) && utf8.RuneCount(w) > inputLen {
if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
strw := string(w)
if _, ok := suggestionsSet[strw]; !ok {
suggestionsSet[strw] = struct{}{}

View File

@@ -4,11 +4,12 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"time"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
"golang.org/x/text/encoding"
)
@@ -42,12 +43,12 @@ func (b *Buffer) Backup(checkTime bool) error {
b.lastbackup = time.Now()
backupdir := config.ConfigDir + "/backups/"
backupdir := filepath.Join(config.ConfigDir, "backups")
if _, err := os.Stat(backupdir); os.IsNotExist(err) {
os.Mkdir(backupdir, os.ModePerm)
}
name := backupdir + util.EscapePath(b.AbsPath)
name := filepath.Join(backupdir, util.EscapePath(b.AbsPath))
err := overwriteFile(name, encoding.Nop, func(file io.Writer) (e error) {
if len(b.lines) == 0 {
@@ -81,7 +82,7 @@ func (b *Buffer) RemoveBackup() {
if !b.Settings["backup"].(bool) || b.Path == "" || b.Type != BTDefault {
return
}
f := config.ConfigDir + "/backups/" + util.EscapePath(b.AbsPath)
f := filepath.Join(config.ConfigDir, "backups", util.EscapePath(b.AbsPath))
os.Remove(f)
}
@@ -89,7 +90,7 @@ func (b *Buffer) RemoveBackup() {
// Returns true if a backup was applied
func (b *Buffer) ApplyBackup(fsize int64) bool {
if b.Settings["backup"].(bool) && len(b.Path) > 0 && b.Type == BTDefault {
backupfile := config.ConfigDir + "/backups/" + util.EscapePath(b.AbsPath)
backupfile := filepath.Join(config.ConfigDir, "backups", util.EscapePath(b.AbsPath))
if info, err := os.Stat(backupfile); err == nil {
backup, err := os.Open(backupfile)
if err == nil {

View File

@@ -1,27 +1,29 @@
package buffer
import (
"bufio"
"bytes"
"crypto/md5"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"unicode/utf8"
luar "layeh.com/gopher-luar"
"github.com/zyedidia/micro/internal/config"
ulua "github.com/zyedidia/micro/internal/lua"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/pkg/highlight"
dmp "github.com/sergi/go-diff/diffmatchpatch"
"github.com/zyedidia/micro/v2/internal/config"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/micro/v2/pkg/highlight"
"golang.org/x/text/encoding/htmlindex"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
@@ -58,6 +60,9 @@ var (
BTRaw = BufType{4, false, true, false}
// BTInfo is a buffer for inputting information
BTInfo = BufType{5, false, true, false}
// BTStdout is a buffer that only writes to stdout
// when closed
BTStdout = BufType{6, false, true, true}
// ErrFileTooLarge is returned when the file is too large to hash
// (fastdirty is automatically enabled)
@@ -73,13 +78,53 @@ type SharedBuffer struct {
// Type of the buffer (e.g. help, raw, scratch etc..)
Type BufType
// Path to the file on disk
Path string
// Absolute path to the file on disk
AbsPath string
// Name of the buffer on the status line
name string
toStdout bool
// Settings customized by the user
Settings map[string]interface{}
Suggestions []string
Completions []string
CurSuggestion int
Messages []*Message
updateDiffTimer *time.Timer
diffBase []byte
diffBaseLineCount int
diffLock sync.RWMutex
diff map[int]DiffStatus
// counts the number of edits
// resets every backupTime edits
lastbackup time.Time
// ReloadDisabled allows the user to disable reloads if they
// are viewing a file that is constantly changing
ReloadDisabled bool
isModified bool
// Whether or not suggestions can be autocompleted must be shared because
// it changes based on how the buffer has changed
HasSuggestions bool
// Modifications is the list of modified regions for syntax highlighting
Modifications []Loc
// The Highlighter struct actually performs the highlighting
Highlighter *highlight.Highlighter
// SyntaxDef represents the syntax highlighting definition being used
// This stores the highlighting rules and filetype detection info
SyntaxDef *highlight.Def
ModifiedThisFrame bool
// Hash of the original buffer -- empty if fastdirty is on
origHash [md5.Size]byte
}
func (b *SharedBuffer) insert(pos Loc, value []byte) {
@@ -87,17 +132,49 @@ func (b *SharedBuffer) insert(pos Loc, value []byte) {
b.HasSuggestions = false
b.LineArray.insert(pos, value)
// b.Modifications is cleared every screen redraw so it's
// ok to append duplicates
b.Modifications = append(b.Modifications, Loc{pos.Y, pos.Y + bytes.Count(value, []byte{'\n'})})
inslines := bytes.Count(value, []byte{'\n'})
b.MarkModified(pos.Y, pos.Y+inslines)
}
func (b *SharedBuffer) remove(start, end Loc) []byte {
b.isModified = true
b.HasSuggestions = false
b.Modifications = append(b.Modifications, Loc{start.Y, start.Y})
defer b.MarkModified(start.Y, end.Y)
return b.LineArray.remove(start, end)
}
// MarkModified marks the buffer as modified for this frame
// and performs rehighlighting if syntax highlighting is enabled
func (b *SharedBuffer) MarkModified(start, end int) {
b.ModifiedThisFrame = true
if !b.Settings["syntax"].(bool) || b.SyntaxDef == nil {
return
}
start = util.Clamp(start, 0, len(b.lines)-1)
end = util.Clamp(end, 0, len(b.lines)-1)
l := -1
for i := start; i <= end; i++ {
l = util.Max(b.Highlighter.ReHighlightStates(b, i), l)
}
b.Highlighter.HighlightMatches(b, start, l)
}
// DisableReload disables future reloads of this sharedbuffer
func (b *SharedBuffer) DisableReload() {
b.ReloadDisabled = true
}
const (
DSUnchanged = 0
DSAdded = 1
DSModified = 2
DSDeletedAbove = 3
)
type DiffStatus byte
// Buffer stores the main information about a currently open file including
// the actual text (in a LineArray), the undo/redo stack (in an EventHandler)
// all the cursors, the syntax highlighting info, the settings for the buffer
@@ -112,36 +189,6 @@ type Buffer struct {
cursors []*Cursor
curCursor int
StartCursor Loc
// Path to the file on disk
Path string
// Absolute path to the file on disk
AbsPath string
// Name of the buffer on the status line
name string
// SyntaxDef represents the syntax highlighting definition being used
// This stores the highlighting rules and filetype detection info
SyntaxDef *highlight.Def
// The Highlighter struct actually performs the highlighting
Highlighter *highlight.Highlighter
HighlightLock sync.Mutex
// Hash of the original buffer -- empty if fastdirty is on
origHash [md5.Size]byte
// Settings customized by the user
Settings map[string]interface{}
Suggestions []string
Completions []string
CurSuggestion int
Messages []*Message
// counts the number of edits
// resets every backupTime edits
lastbackup time.Time
}
// NewBufferFromFile opens a new buffer using the given path
@@ -196,22 +243,6 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
b := new(Buffer)
b.Settings = config.DefaultCommonSettings()
for k, v := range config.GlobalSettings {
if _, ok := b.Settings[k]; ok {
b.Settings[k] = v
}
}
config.InitLocalSettings(b.Settings, path)
enc, err := htmlindex.Get(b.Settings["encoding"].(string))
if err != nil {
enc = unicode.UTF8
b.Settings["encoding"] = "utf-8"
}
reader := transform.NewReader(r, enc.NewDecoder())
found := false
if len(path) > 0 {
for _, buf := range OpenBuffers {
@@ -223,28 +254,44 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
}
}
b.Path = path
b.AbsPath = absPath
if !found {
b.SharedBuffer = new(SharedBuffer)
b.Type = btype
b.AbsPath = absPath
b.Path = path
b.Settings = config.DefaultCommonSettings()
for k, v := range config.GlobalSettings {
if _, ok := config.DefaultGlobalOnlySettings[k]; !ok {
// make sure setting is not global-only
b.Settings[k] = v
}
}
config.InitLocalSettings(b.Settings, path)
enc, err := htmlindex.Get(b.Settings["encoding"].(string))
if err != nil {
enc = unicode.UTF8
b.Settings["encoding"] = "utf-8"
}
hasBackup := b.ApplyBackup(size)
if !hasBackup {
reader := bufio.NewReader(transform.NewReader(r, enc.NewDecoder()))
b.LineArray = NewLineArray(uint64(size), FFAuto, reader)
}
b.EventHandler = NewEventHandler(b.SharedBuffer, b.cursors)
// The last time this file was modified
b.UpdateModTime()
}
if b.Settings["readonly"].(bool) && b.Type == BTDefault {
b.Type.Readonly = true
}
// The last time this file was modified
b.UpdateModTime()
switch b.Endings {
case FFUnix:
b.Settings["fileformat"] = "unix"
@@ -253,10 +300,11 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
}
b.UpdateRules()
// init local settings again now that we know the filetype
config.InitLocalSettings(b.Settings, b.Path)
if _, err := os.Stat(config.ConfigDir + "/buffers/"); os.IsNotExist(err) {
os.Mkdir(config.ConfigDir+"/buffers/", os.ModePerm)
if _, err := os.Stat(filepath.Join(config.ConfigDir, "buffers")); os.IsNotExist(err) {
os.Mkdir(filepath.Join(config.ConfigDir, "buffers"), os.ModePerm)
}
if startcursor.X != -1 && startcursor.Y != -1 {
@@ -273,7 +321,7 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
b.AddCursor(NewCursor(b, b.StartCursor))
b.GetActiveCursor().Relocate()
if !b.Settings["fastdirty"].(bool) {
if !b.Settings["fastdirty"].(bool) && !found {
if size > LargeFileThreshold {
// If the file is larger than LargeFileThreshold fastdirty needs to be on
b.Settings["fastdirty"] = true
@@ -282,13 +330,11 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
}
}
err = config.RunPluginFn("onBufferOpen", luar.New(ulua.L, b))
err := config.RunPluginFn("onBufferOpen", luar.New(ulua.L, b))
if err != nil {
screen.TermMessage(err)
}
b.Modifications = make([]Loc, 0, 10)
OpenBuffers = append(OpenBuffers, b)
return b
@@ -314,18 +360,26 @@ func (b *Buffer) Fini() {
b.Serialize()
}
b.RemoveBackup()
if b.Type == BTStdout {
fmt.Fprint(util.Stdout, string(b.Bytes()))
}
}
// GetName returns the name that should be displayed in the statusline
// for this buffer
func (b *Buffer) GetName() string {
if b.name == "" {
name := b.name
if name == "" {
if b.Path == "" {
return "No name"
}
return b.Path
name = b.Path
}
return b.name
if b.Settings["basename"].(bool) {
return path.Base(name)
}
return name
}
//SetName changes the name for this buffer
@@ -355,16 +409,6 @@ func (b *Buffer) Remove(start, end Loc) {
}
}
// ClearModifications clears the list of modified lines in this buffer
// The list of modified lines is used for syntax highlighting so that
// we can selectively highlight only the necessary lines
// This function should be called every time this buffer is drawn to
// the screen
func (b *Buffer) ClearModifications() {
// clear slice without resetting the cap
b.Modifications = b.Modifications[:0]
}
// FileType returns the buffer's filetype
func (b *Buffer) FileType() string {
return b.Settings["filetype"].(string)
@@ -398,7 +442,7 @@ func (b *Buffer) ReOpen() error {
return err
}
reader := transform.NewReader(file, enc.NewDecoder())
reader := bufio.NewReader(transform.NewReader(file, enc.NewDecoder()))
data, err := ioutil.ReadAll(reader)
txt := string(data)
@@ -408,6 +452,9 @@ func (b *Buffer) ReOpen() error {
b.EventHandler.ApplyDiff(txt)
err = b.UpdateModTime()
if !b.Settings["fastdirty"].(bool) {
calcHash(b, &b.origHash)
}
b.isModified = false
b.RelocateCursors()
return err
@@ -426,7 +473,7 @@ func (b *Buffer) RuneAt(loc Loc) rune {
if len(line) > 0 {
i := 0
for len(line) > 0 {
r, size := utf8.DecodeRune(line)
r, _, size := util.DecodeCharacter(line)
line = line[size:]
i++
@@ -500,7 +547,37 @@ func (b *Buffer) UpdateRules() {
return
}
syntaxFile := ""
foundDef := false
var header *highlight.Header
// search for the syntax file in the user's custom syntax files
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
data, err := f.Data()
if err != nil {
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
continue
}
header, err = highlight.MakeHeaderYaml(data)
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
if ((ft == "unknown" || ft == "") && highlight.MatchFiletype(header.FtDetect, b.Path, b.lines[0].data)) || header.FileType == ft {
syndef, err := highlight.ParseDef(file, header)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
b.SyntaxDef = syndef
syntaxFile = f.Name()
foundDef = true
break
}
}
// search in the default syntax files
for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
data, err := f.Data()
if err != nil {
@@ -525,34 +602,8 @@ func (b *Buffer) UpdateRules() {
}
}
if syntaxFile == "" {
// search for the syntax file in the user's custom syntax files
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
log.Println("real runtime file", f.Name())
data, err := f.Data()
if err != nil {
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
continue
}
header, err = highlight.MakeHeaderYaml(data)
file, err := highlight.ParseFile(data)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
if ((ft == "unknown" || ft == "") && highlight.MatchFiletype(header.FtDetect, b.Path, b.lines[0].data)) || header.FileType == ft {
syndef, err := highlight.ParseDef(file, header)
if err != nil {
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
continue
}
b.SyntaxDef = syndef
break
}
}
} else {
if syntaxFile != "" && !foundDef {
// we found a syntax file using a syntax header file
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
if f.Name() == syntaxFile {
data, err := f.Data()
@@ -627,7 +678,7 @@ func (b *Buffer) UpdateRules() {
go func() {
b.Highlighter.HighlightStates(b)
b.Highlighter.HighlightMatches(b, 0, b.End().Y)
screen.DrawChan <- true
screen.Redraw()
}()
}
}
@@ -757,7 +808,7 @@ func (b *Buffer) MoveLinesUp(start int, end int) {
if end == len(b.lines) {
b.Insert(
Loc{
utf8.RuneCount(b.lines[end-1].data),
util.CharacterCount(b.lines[end-1].data),
end - 1,
},
"\n"+l,
@@ -804,7 +855,7 @@ var BracePairs = [][2]rune{
// returns the location of the matching brace
// if the boolean returned is true then the original matching brace is one character left
// of the starting location
func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool) {
func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool, bool) {
curLine := []rune(string(b.LineBytes(start.Y)))
startChar := ' '
if start.X >= 0 && start.X < len(curLine) {
@@ -834,9 +885,9 @@ func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool) {
i--
if i == 0 {
if startChar == braceType[0] {
return Loc{x, y}, false
return Loc{x, y}, false, true
}
return Loc{x, y}, true
return Loc{x, y}, true, true
}
}
}
@@ -858,9 +909,9 @@ func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool) {
i--
if i == 0 {
if leftChar == braceType[1] {
return Loc{x, y}, true
return Loc{x, y}, true, true
}
return Loc{x, y}, false
return Loc{x, y}, false, true
}
} else if r == braceType[1] {
i++
@@ -868,7 +919,7 @@ func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool) {
}
}
}
return start, true
return start, true, false
}
// Retab changes all tabs to spaces or vice versa
@@ -891,6 +942,7 @@ func (b *Buffer) Retab() {
l = bytes.TrimLeft(l, " \t")
b.lines[i].data = append(ws, l...)
b.MarkModified(i, i)
dirty = true
}
@@ -932,6 +984,101 @@ func (b *Buffer) Write(bytes []byte) (n int, err error) {
return len(bytes), nil
}
func (b *Buffer) updateDiffSync() {
b.diffLock.Lock()
defer b.diffLock.Unlock()
b.diff = make(map[int]DiffStatus)
if b.diffBase == nil {
return
}
differ := dmp.New()
baseRunes, bufferRunes, _ := differ.DiffLinesToRunes(string(b.diffBase), string(b.Bytes()))
diffs := differ.DiffMainRunes(baseRunes, bufferRunes, false)
lineN := 0
for _, diff := range diffs {
lineCount := len([]rune(diff.Text))
switch diff.Type {
case dmp.DiffEqual:
lineN += lineCount
case dmp.DiffInsert:
var status DiffStatus
if b.diff[lineN] == DSDeletedAbove {
status = DSModified
} else {
status = DSAdded
}
for i := 0; i < lineCount; i++ {
b.diff[lineN] = status
lineN++
}
case dmp.DiffDelete:
b.diff[lineN] = DSDeletedAbove
}
}
}
// UpdateDiff computes the diff between the diff base and the buffer content.
// The update may be performed synchronously or asynchronously.
// UpdateDiff calls the supplied callback when the update is complete.
// The argument passed to the callback is set to true if and only if
// the update was performed synchronously.
// If an asynchronous update is already pending when UpdateDiff is called,
// UpdateDiff does not schedule another update, in which case the callback
// is not called.
func (b *Buffer) UpdateDiff(callback func(bool)) {
if b.updateDiffTimer != nil {
return
}
lineCount := b.LinesNum()
if b.diffBaseLineCount > lineCount {
lineCount = b.diffBaseLineCount
}
if lineCount < 1000 {
b.updateDiffSync()
callback(true)
} else if lineCount < 30000 {
b.updateDiffTimer = time.AfterFunc(500*time.Millisecond, func() {
b.updateDiffTimer = nil
b.updateDiffSync()
callback(false)
})
} else {
// Don't compute diffs for very large files
b.diffLock.Lock()
b.diff = make(map[int]DiffStatus)
b.diffLock.Unlock()
callback(true)
}
}
// SetDiffBase sets the text that is used as the base for diffing the buffer content
func (b *Buffer) SetDiffBase(diffBase []byte) {
b.diffBase = diffBase
if diffBase == nil {
b.diffBaseLineCount = 0
} else {
b.diffBaseLineCount = strings.Count(string(diffBase), "\n")
}
b.UpdateDiff(func(synchronous bool) {
screen.Redraw()
})
}
// DiffStatus returns the diff status for a line in the buffer
func (b *Buffer) DiffStatus(lineN int) DiffStatus {
b.diffLock.RLock()
defer b.diffLock.RUnlock()
// Note that the zero value for DiffStatus is equal to DSUnchanged
return b.diff[lineN]
}
// WriteLog writes a string to the log buffer
func WriteLog(s string) {
LogBuf.EventHandler.Insert(LogBuf.End(), s)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,322 @@
package buffer
import (
"math/rand"
"strings"
"testing"
"github.com/stretchr/testify/assert"
lua "github.com/yuin/gopher-lua"
"github.com/zyedidia/micro/v2/internal/config"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/util"
)
type operation struct {
start Loc
end Loc
text []string
}
func init() {
ulua.L = lua.NewState()
config.InitGlobalSettings()
config.GlobalSettings["backup"] = false
config.GlobalSettings["fastdirty"] = true
}
func check(t *testing.T, before []string, operations []operation, after []string) {
assert := assert.New(t)
b := NewBufferFromString(strings.Join(before, "\n"), "", BTDefault)
assert.NotEqual("", b.GetName())
assert.Equal(false, b.ExternallyModified())
assert.Equal(false, b.Modified())
assert.Equal(1, b.NumCursors())
checkText := func(lines []string) {
assert.Equal([]byte(strings.Join(lines, "\n")), b.Bytes())
assert.Equal(len(lines), b.LinesNum())
for i, s := range lines {
assert.Equal(s, b.Line(i))
assert.Equal([]byte(s), b.LineBytes(i))
}
}
checkText(before)
var cursors []*Cursor
for _, op := range operations {
cursor := NewCursor(b, op.start)
cursor.SetSelectionStart(op.start)
cursor.SetSelectionEnd(op.end)
b.AddCursor(cursor)
cursors = append(cursors, cursor)
}
assert.Equal(1+len(operations), b.NumCursors())
for i, op := range operations {
cursor := cursors[i]
b.SetCurCursor(cursor.Num)
cursor.DeleteSelection()
b.Insert(cursor.Loc, strings.Join(op.text, "\n"))
}
checkText(after)
// must have exactly two events per operation (delete and insert)
for range operations {
b.UndoOneEvent()
b.UndoOneEvent()
}
checkText(before)
for i, op := range operations {
cursor := cursors[i]
if op.start == op.end {
assert.Equal(op.start, cursor.Loc)
} else {
assert.Equal(op.start, cursor.CurSelection[0])
assert.Equal(op.end, cursor.CurSelection[1])
}
}
for range operations {
b.RedoOneEvent()
b.RedoOneEvent()
}
checkText(after)
b.Close()
}
const maxLineLength = 200
var alphabet = []rune(" abcdeäم📚")
func randomString(length int) string {
runes := make([]rune, length)
for i := range runes {
runes[i] = alphabet[rand.Intn(len(alphabet))]
}
return string(runes)
}
func randomText(nLines int) string {
lines := make([]string, nLines)
for i := range lines {
lines[i] = randomString(rand.Intn(maxLineLength + 1))
}
return strings.Join(lines, "\n")
}
func benchCreateAndClose(testingB *testing.B, nLines int) {
rand.Seed(int64(nLines))
text := randomText(nLines)
testingB.ResetTimer()
for i := 0; i < testingB.N; i++ {
b := NewBufferFromString(text, "", BTDefault)
b.Close()
}
}
func benchRead(testingB *testing.B, nLines int) {
rand.Seed(int64(nLines))
b := NewBufferFromString(randomText(nLines), "", BTDefault)
testingB.ResetTimer()
for i := 0; i < testingB.N; i++ {
b.Bytes()
for j := 0; j < b.LinesNum(); j++ {
b.Line(j)
b.LineBytes(j)
}
}
testingB.StopTimer()
b.Close()
}
func benchEdit(testingB *testing.B, nLines, nCursors int) {
rand.Seed(int64(nLines + nCursors))
b := NewBufferFromString(randomText(nLines), "", BTDefault)
regionSize := nLines / nCursors
operations := make([]operation, nCursors)
for i := range operations {
startLine := (i * regionSize) + rand.Intn(regionSize-5)
startColumn := rand.Intn(util.CharacterCountInString(b.Line(startLine)) + 1)
endLine := startLine + 1 + rand.Intn(5)
endColumn := rand.Intn(util.CharacterCountInString(b.Line(endLine)) + 1)
operations[i] = operation{
start: Loc{startColumn, startLine},
end: Loc{endColumn, endLine},
text: []string{randomText(2 + rand.Intn(4))},
}
}
testingB.ResetTimer()
for i := 0; i < testingB.N; i++ {
b.SetCursors([]*Cursor{})
var cursors []*Cursor
for _, op := range operations {
cursor := NewCursor(b, op.start)
cursor.SetSelectionStart(op.start)
cursor.SetSelectionEnd(op.end)
b.AddCursor(cursor)
cursors = append(cursors, cursor)
}
for j, op := range operations {
cursor := cursors[j]
b.SetCurCursor(cursor.Num)
cursor.DeleteSelection()
b.Insert(cursor.Loc, op.text[0])
}
for b.UndoStack.Peek() != nil {
b.UndoOneEvent()
}
}
testingB.StopTimer()
b.Close()
}
func BenchmarkCreateAndClose10Lines(b *testing.B) {
benchCreateAndClose(b, 10)
}
func BenchmarkCreateAndClose100Lines(b *testing.B) {
benchCreateAndClose(b, 100)
}
func BenchmarkCreateAndClose1000Lines(b *testing.B) {
benchCreateAndClose(b, 1000)
}
func BenchmarkCreateAndClose10000Lines(b *testing.B) {
benchCreateAndClose(b, 10000)
}
func BenchmarkCreateAndClose100000Lines(b *testing.B) {
benchCreateAndClose(b, 100000)
}
func BenchmarkCreateAndClose1000000Lines(b *testing.B) {
benchCreateAndClose(b, 1000000)
}
func BenchmarkRead10Lines(b *testing.B) {
benchRead(b, 10)
}
func BenchmarkRead100Lines(b *testing.B) {
benchRead(b, 100)
}
func BenchmarkRead1000Lines(b *testing.B) {
benchRead(b, 1000)
}
func BenchmarkRead10000Lines(b *testing.B) {
benchRead(b, 10000)
}
func BenchmarkRead100000Lines(b *testing.B) {
benchRead(b, 100000)
}
func BenchmarkRead1000000Lines(b *testing.B) {
benchRead(b, 1000000)
}
func BenchmarkEdit10Lines1Cursor(b *testing.B) {
benchEdit(b, 10, 1)
}
func BenchmarkEdit100Lines1Cursor(b *testing.B) {
benchEdit(b, 100, 1)
}
func BenchmarkEdit100Lines10Cursors(b *testing.B) {
benchEdit(b, 100, 10)
}
func BenchmarkEdit1000Lines1Cursor(b *testing.B) {
benchEdit(b, 1000, 1)
}
func BenchmarkEdit1000Lines10Cursors(b *testing.B) {
benchEdit(b, 1000, 10)
}
func BenchmarkEdit1000Lines100Cursors(b *testing.B) {
benchEdit(b, 1000, 100)
}
func BenchmarkEdit10000Lines1Cursor(b *testing.B) {
benchEdit(b, 10000, 1)
}
func BenchmarkEdit10000Lines10Cursors(b *testing.B) {
benchEdit(b, 10000, 10)
}
func BenchmarkEdit10000Lines100Cursors(b *testing.B) {
benchEdit(b, 10000, 100)
}
func BenchmarkEdit10000Lines1000Cursors(b *testing.B) {
benchEdit(b, 10000, 1000)
}
func BenchmarkEdit100000Lines1Cursor(b *testing.B) {
benchEdit(b, 100000, 1)
}
func BenchmarkEdit100000Lines10Cursors(b *testing.B) {
benchEdit(b, 100000, 10)
}
func BenchmarkEdit100000Lines100Cursors(b *testing.B) {
benchEdit(b, 100000, 100)
}
func BenchmarkEdit100000Lines1000Cursors(b *testing.B) {
benchEdit(b, 100000, 1000)
}
func BenchmarkEdit1000000Lines1Cursor(b *testing.B) {
benchEdit(b, 1000000, 1)
}
func BenchmarkEdit1000000Lines10Cursors(b *testing.B) {
benchEdit(b, 1000000, 10)
}
func BenchmarkEdit1000000Lines100Cursors(b *testing.B) {
benchEdit(b, 1000000, 100)
}
func BenchmarkEdit1000000Lines1000Cursors(b *testing.B) {
benchEdit(b, 1000000, 1000)
}

View File

@@ -1,15 +1,13 @@
package buffer
import (
"unicode/utf8"
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/util"
)
// InBounds returns whether the given location is a valid character position in the given buffer
func InBounds(pos Loc, buf *Buffer) bool {
if pos.Y < 0 || pos.Y >= len(buf.lines) || pos.X < 0 || pos.X > utf8.RuneCount(buf.LineBytes(pos.Y)) {
if pos.Y < 0 || pos.Y >= len(buf.lines) || pos.X < 0 || pos.X > util.CharacterCount(buf.LineBytes(pos.Y)) {
return false
}
@@ -76,8 +74,8 @@ func (c *Cursor) GetVisualX() int {
bytes := c.buf.LineBytes(c.Y)
tabsize := int(c.buf.Settings["tabsize"].(float64))
if c.X > utf8.RuneCount(bytes) {
c.X = utf8.RuneCount(bytes) - 1
if c.X > util.CharacterCount(bytes) {
c.X = util.CharacterCount(bytes) - 1
}
return util.StringWidth(bytes, c.X, tabsize)
@@ -102,16 +100,29 @@ func (c *Cursor) Start() {
func (c *Cursor) StartOfText() {
c.Start()
for util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == utf8.RuneCount(c.buf.LineBytes(c.Y)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
break
}
c.Right()
}
}
// IsStartOfText returns whether the cursor is at the first
// non-whitespace rune of the line it is on
func (c *Cursor) IsStartOfText() bool {
x := 0
for util.IsWhitespace(c.RuneUnder(x)) {
if x == util.CharacterCount(c.buf.LineBytes(c.Y)) {
break
}
x++
}
return c.X == x
}
// End moves the cursor to the end of the line it is on
func (c *Cursor) End() {
c.X = utf8.RuneCount(c.buf.LineBytes(c.Y))
c.X = util.CharacterCount(c.buf.LineBytes(c.Y))
c.LastVisualX = c.GetVisualX()
}
@@ -229,8 +240,8 @@ func (c *Cursor) UpN(amount int) {
bytes := c.buf.LineBytes(proposedY)
c.X = c.GetCharPosInLine(bytes, c.LastVisualX)
if c.X > utf8.RuneCount(bytes) || (amount < 0 && proposedY == c.Y) {
c.X = utf8.RuneCount(bytes)
if c.X > util.CharacterCount(bytes) || (amount < 0 && proposedY == c.Y) {
c.X = util.CharacterCount(bytes)
}
c.Y = proposedY
@@ -272,7 +283,7 @@ func (c *Cursor) Right() {
if c.Loc == c.buf.End() {
return
}
if c.X < utf8.RuneCount(c.buf.LineBytes(c.Y)) {
if c.X < util.CharacterCount(c.buf.LineBytes(c.Y)) {
c.X++
} else {
c.Down()
@@ -293,8 +304,8 @@ func (c *Cursor) Relocate() {
if c.X < 0 {
c.X = 0
} else if c.X > utf8.RuneCount(c.buf.LineBytes(c.Y)) {
c.X = utf8.RuneCount(c.buf.LineBytes(c.Y))
} else if c.X > util.CharacterCount(c.buf.LineBytes(c.Y)) {
c.X = util.CharacterCount(c.buf.LineBytes(c.Y))
}
}
@@ -320,7 +331,7 @@ func (c *Cursor) SelectWord() {
c.SetSelectionStart(Loc{backward, c.Y})
c.OrigSelection[0] = c.CurSelection[0]
lineLen := utf8.RuneCount(c.buf.LineBytes(c.Y)) - 1
lineLen := util.CharacterCount(c.buf.LineBytes(c.Y)) - 1
for forward < lineLen && util.IsWordChar(c.RuneUnder(forward+1)) {
forward++
}
@@ -352,7 +363,7 @@ func (c *Cursor) AddWordToSelection() {
if c.Loc.GreaterThan(c.OrigSelection[1]) {
forward := c.X
lineLen := utf8.RuneCount(c.buf.LineBytes(c.Y)) - 1
lineLen := util.CharacterCount(c.buf.LineBytes(c.Y)) - 1
for forward < lineLen && util.IsWordChar(c.RuneUnder(forward+1)) {
forward++
}
@@ -379,7 +390,7 @@ func (c *Cursor) SelectTo(loc Loc) {
// WordRight moves the cursor one word to the right
func (c *Cursor) WordRight() {
for util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == utf8.RuneCount(c.buf.LineBytes(c.Y)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
c.Right()
return
}
@@ -387,7 +398,7 @@ func (c *Cursor) WordRight() {
}
c.Right()
for util.IsWordChar(c.RuneUnder(c.X)) {
if c.X == utf8.RuneCount(c.buf.LineBytes(c.Y)) {
if c.X == util.CharacterCount(c.buf.LineBytes(c.Y)) {
return
}
c.Right()
@@ -416,14 +427,14 @@ func (c *Cursor) WordLeft() {
// RuneUnder returns the rune under the given x position
func (c *Cursor) RuneUnder(x int) rune {
line := c.buf.LineBytes(c.Y)
if len(line) == 0 || x >= utf8.RuneCount(line) {
if len(line) == 0 || x >= util.CharacterCount(line) {
return '\n'
} else if x < 0 {
x = 0
}
i := 0
for len(line) > 0 {
r, size := utf8.DecodeRune(line)
r, _, size := util.DecodeCharacter(line)
line = line[size:]
if i == x {

View File

@@ -2,14 +2,13 @@ package buffer
import (
"bytes"
"log"
"time"
"unicode/utf8"
dmp "github.com/sergi/go-diff/diffmatchpatch"
"github.com/zyedidia/micro/internal/config"
ulua "github.com/zyedidia/micro/internal/lua"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/v2/internal/config"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
luar "layeh.com/gopher-luar"
)
@@ -42,6 +41,73 @@ type Delta struct {
End Loc
}
// DoTextEvent runs a text event
func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) {
oldl := eh.buf.LinesNum()
if useUndo {
eh.Execute(t)
} else {
ExecuteTextEvent(t, eh.buf)
}
if len(t.Deltas) != 1 {
return
}
text := t.Deltas[0].Text
start := t.Deltas[0].Start
lastnl := -1
var endX int
var textX int
if t.EventType == TextEventInsert {
linecount := eh.buf.LinesNum() - oldl
textcount := util.CharacterCount(text)
lastnl = bytes.LastIndex(text, []byte{'\n'})
if lastnl >= 0 {
endX = util.CharacterCount(text[lastnl+1:])
textX = endX
} else {
endX = start.X + textcount
textX = textcount
}
t.Deltas[0].End = clamp(Loc{endX, start.Y + linecount}, eh.buf.LineArray)
}
end := t.Deltas[0].End
for _, c := range eh.cursors {
move := func(loc Loc) Loc {
if t.EventType == TextEventInsert {
if start.Y != loc.Y && loc.GreaterThan(start) {
loc.Y += end.Y - start.Y
} else if loc.Y == start.Y && loc.GreaterEqual(start) {
loc.Y += end.Y - start.Y
if lastnl >= 0 {
loc.X += textX - start.X
} else {
loc.X += textX
}
}
return loc
} else {
if loc.Y != end.Y && loc.GreaterThan(end) {
loc.Y -= end.Y - start.Y
} else if loc.Y == end.Y && loc.GreaterEqual(end) {
loc = loc.MoveLA(-DiffLA(start, end, eh.buf.LineArray), eh.buf.LineArray)
}
return loc
}
}
c.Loc = move(c.Loc)
c.CurSelection[0] = move(c.CurSelection[0])
c.CurSelection[1] = move(c.CurSelection[1])
c.OrigSelection[0] = move(c.OrigSelection[0])
c.OrigSelection[1] = move(c.OrigSelection[1])
c.Relocate()
c.LastVisualX = c.GetVisualX()
}
}
// ExecuteTextEvent runs a text event
func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) {
if t.EventType == TextEventInsert {
@@ -57,7 +123,7 @@ func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) {
t.Deltas[i].Text = buf.remove(d.Start, d.End)
buf.insert(d.Start, d.Text)
t.Deltas[i].Start = d.Start
t.Deltas[i].End = Loc{d.Start.X + utf8.RuneCount(d.Text), d.Start.Y}
t.Deltas[i].End = Loc{d.Start.X + util.CharacterCount(d.Text), d.Start.Y}
}
for i, j := 0, len(t.Deltas)-1; i < j; i, j = i+1, j-1 {
t.Deltas[i], t.Deltas[j] = t.Deltas[j], t.Deltas[i]
@@ -66,9 +132,9 @@ func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) {
}
// UndoTextEvent undoes a text event
func UndoTextEvent(t *TextEvent, buf *SharedBuffer) {
func (eh *EventHandler) UndoTextEvent(t *TextEvent) {
t.EventType = -t.EventType
ExecuteTextEvent(t, buf)
eh.DoTextEvent(t, false)
}
// EventHandler executes text manipulations and allows undoing and redoing
@@ -100,12 +166,12 @@ func (eh *EventHandler) ApplyDiff(new string) {
loc := eh.buf.Start()
for _, d := range diff {
if d.Type == dmp.DiffDelete {
eh.Remove(loc, loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf.LineArray))
eh.Remove(loc, loc.MoveLA(util.CharacterCountInString(d.Text), eh.buf.LineArray))
} else {
if d.Type == dmp.DiffInsert {
eh.Insert(loc, d.Text)
}
loc = loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf.LineArray)
loc = loc.MoveLA(util.CharacterCountInString(d.Text), eh.buf.LineArray)
}
}
}
@@ -118,78 +184,33 @@ func (eh *EventHandler) Insert(start Loc, textStr string) {
// InsertBytes creates an insert text event and executes it
func (eh *EventHandler) InsertBytes(start Loc, text []byte) {
if len(text) == 0 {
return
}
start = clamp(start, eh.buf.LineArray)
e := &TextEvent{
C: *eh.cursors[eh.active],
EventType: TextEventInsert,
Deltas: []Delta{{text, start, Loc{0, 0}}},
Time: time.Now(),
}
// oldl := eh.buf.LinesNum()
eh.Execute(e)
// linecount := eh.buf.LinesNum() - oldl
textcount := utf8.RuneCount(text)
lastnl := bytes.LastIndex(text, []byte{'\n'})
var endX int
var textX int
if lastnl >= 0 {
endX = utf8.RuneCount(text[lastnl:])
textX = endX
} else {
// endX = start.X + textcount
textX = textcount
}
e.Deltas[0].End = start.MoveLA(textcount, eh.buf.LineArray)
// e.Deltas[0].End = clamp(Loc{endX, start.Y + linecount}, eh.buf.LineArray)
end := e.Deltas[0].End
for _, c := range eh.cursors {
move := func(loc Loc) Loc {
log.Println("move", loc)
if start.Y != end.Y && loc.GreaterThan(start) {
loc.Y += end.Y - start.Y
} else if loc.Y == start.Y && loc.GreaterEqual(start) {
loc.Y += end.Y - start.Y
loc.X += textX
}
return loc
}
c.Loc = move(c.Loc)
c.Relocate()
c.CurSelection[0] = move(c.CurSelection[0])
c.CurSelection[1] = move(c.CurSelection[1])
c.OrigSelection[0] = move(c.OrigSelection[0])
c.OrigSelection[1] = move(c.OrigSelection[1])
c.LastVisualX = c.GetVisualX()
}
eh.DoTextEvent(e, true)
}
// Remove creates a remove text event and executes it
func (eh *EventHandler) Remove(start, end Loc) {
if start == end {
return
}
start = clamp(start, eh.buf.LineArray)
end = clamp(end, eh.buf.LineArray)
e := &TextEvent{
C: *eh.cursors[eh.active],
EventType: TextEventRemove,
Deltas: []Delta{{[]byte{}, start, end}},
Time: time.Now(),
}
eh.Execute(e)
for _, c := range eh.cursors {
move := func(loc Loc) Loc {
if start.Y != end.Y && loc.GreaterThan(end) {
loc.Y -= end.Y - start.Y
} else if loc.Y == end.Y && loc.GreaterEqual(end) {
loc = loc.MoveLA(-DiffLA(start, end, eh.buf.LineArray), eh.buf.LineArray)
}
return loc
}
c.Loc = move(c.Loc)
c.CurSelection[0] = move(c.CurSelection[0])
c.CurSelection[1] = move(c.CurSelection[1])
c.OrigSelection[0] = move(c.OrigSelection[0])
c.OrigSelection[1] = move(c.OrigSelection[1])
c.LastVisualX = c.GetVisualX()
}
eh.DoTextEvent(e, true)
}
// MultipleReplace creates an multiple insertions executes them
@@ -260,10 +281,9 @@ func (eh *EventHandler) UndoOneEvent() {
if t == nil {
return
}
// Undo it
// Modifies the text event
UndoTextEvent(t, eh.buf)
eh.UndoTextEvent(t)
// Set the cursor in the right place
teCursor := t.C
@@ -309,9 +329,6 @@ func (eh *EventHandler) RedoOneEvent() {
return
}
// Modifies the text event
UndoTextEvent(t, eh.buf)
teCursor := t.C
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
t.C = *eh.cursors[teCursor.Num]
@@ -320,5 +337,8 @@ func (eh *EventHandler) RedoOneEvent() {
teCursor.Num = -1
}
// Modifies the text event
eh.UndoTextEvent(t)
eh.UndoStack.Push(t)
}

View File

@@ -2,11 +2,12 @@ package buffer
import (
"bufio"
"bytes"
"io"
"sync"
"unicode/utf8"
"github.com/zyedidia/micro/pkg/highlight"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/micro/v2/pkg/highlight"
)
// Finds the byte index of the nth rune in a byte slice
@@ -18,7 +19,7 @@ func runeToByteIndex(n int, txt []byte) int {
count := 0
i := 0
for len(txt) > 0 {
_, size := utf8.DecodeRune(txt)
_, _, size := util.DecodeCharacter(txt)
txt = txt[size:]
count += size
@@ -152,17 +153,19 @@ func NewLineArray(size uint64, endings FileFormat, reader io.Reader) *LineArray
// Bytes returns the string that should be written to disk when
// the line array is saved
func (la *LineArray) Bytes() []byte {
str := make([]byte, 0, la.initsize+1000) // initsize should provide a good estimate
b := new(bytes.Buffer)
// initsize should provide a good estimate
b.Grow(int(la.initsize + 4096))
for i, l := range la.lines {
str = append(str, l.data...)
b.Write(l.data)
if i != len(la.lines)-1 {
if la.Endings == FFDos {
str = append(str, '\r')
b.WriteByte('\r')
}
str = append(str, '\n')
b.WriteByte('\n')
}
}
return str
return b.Bytes()
}
// newlineBelow adds a newline below the given line number
@@ -230,9 +233,7 @@ func (la *LineArray) remove(start, end Loc) []byte {
if start.Y == end.Y {
la.lines[start.Y].data = append(la.lines[start.Y].data[:startX], la.lines[start.Y].data[endX:]...)
} else {
for i := start.Y + 1; i <= end.Y-1; i++ {
la.deleteLine(start.Y + 1)
}
la.deleteLines(start.Y+1, end.Y-1)
la.deleteToEnd(Loc{startX, start.Y})
la.deleteFromStart(Loc{endX - 1, start.Y + 1})
la.joinLines(start.Y, start.Y+1)
@@ -255,6 +256,10 @@ func (la *LineArray) deleteLine(y int) {
la.lines = la.lines[:y+copy(la.lines[y:], la.lines[y+1:])]
}
func (la *LineArray) deleteLines(y1, y2 int) {
la.lines = la.lines[:y1+copy(la.lines[y1:], la.lines[y2+1:])]
}
// DeleteByte deletes the byte at a position
func (la *LineArray) deleteByte(pos Loc) {
la.lines[pos.Y].data = la.lines[pos.Y].data[:pos.X+copy(la.lines[pos.Y].data[pos.X:], la.lines[pos.Y].data[pos.X+1:])]
@@ -294,7 +299,7 @@ func (la *LineArray) Start() Loc {
// End returns the location of the last character in the buffer
func (la *LineArray) End() Loc {
numlines := len(la.lines)
return Loc{utf8.RuneCount(la.lines[numlines-1].data), numlines - 1}
return Loc{util.CharacterCount(la.lines[numlines-1].data), numlines - 1}
}
// LineBytes returns line n as an array of bytes

View File

@@ -1,9 +1,7 @@
package buffer
import (
"unicode/utf8"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/util"
)
// Loc stores a location
@@ -68,9 +66,9 @@ func DiffLA(a, b Loc, buf *LineArray) int {
loc := 0
for i := a.Y + 1; i < b.Y; i++ {
// + 1 for the newline
loc += utf8.RuneCount(buf.LineBytes(i)) + 1
loc += util.CharacterCount(buf.LineBytes(i)) + 1
}
loc += utf8.RuneCount(buf.LineBytes(a.Y)) - a.X + b.X + 1
loc += util.CharacterCount(buf.LineBytes(a.Y)) - a.X + b.X + 1
return loc
}
@@ -80,7 +78,7 @@ func (l Loc) right(buf *LineArray) Loc {
return Loc{l.X + 1, l.Y}
}
var res Loc
if l.X < utf8.RuneCount(buf.LineBytes(l.Y)) {
if l.X < util.CharacterCount(buf.LineBytes(l.Y)) {
res = Loc{l.X + 1, l.Y}
} else {
res = Loc{0, l.Y + 1}
@@ -97,12 +95,12 @@ func (l Loc) left(buf *LineArray) Loc {
if l.X > 0 {
res = Loc{l.X - 1, l.Y}
} else {
res = Loc{utf8.RuneCount(buf.LineBytes(l.Y - 1)), l.Y - 1}
res = Loc{util.CharacterCount(buf.LineBytes(l.Y - 1)), l.Y - 1}
}
return res
}
// Move moves the cursor n characters to the left or right
// MoveLA moves the cursor n characters to the left or right
// It moves the cursor left if n is negative
func (l Loc) MoveLA(n int, buf *LineArray) Loc {
if n > 0 {
@@ -117,9 +115,12 @@ func (l Loc) MoveLA(n int, buf *LineArray) Loc {
return l
}
func (l Loc) Diff(a, b Loc, buf *Buffer) int {
return DiffLA(a, b, buf.LineArray)
// Diff returns the difference between two locs
func (l Loc) Diff(b Loc, buf *Buffer) int {
return DiffLA(l, b, buf.LineArray)
}
// Move moves a loc n characters
func (l Loc) Move(n int, buf *Buffer) Loc {
return l.MoveLA(n, buf.LineArray)
}
@@ -139,7 +140,7 @@ func ByteOffset(pos Loc, buf *Buffer) int {
// clamps a loc within a buffer
func clamp(pos Loc, la *LineArray) Loc {
if pos.GreaterEqual(la.End()) {
return la.End().MoveLA(-1, la)
return la.End()
} else if pos.LessThan(la.Start()) {
return la.Start()
}

View File

@@ -1,7 +1,7 @@
package buffer
import (
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/tcell"
)

View File

@@ -1,6 +1,7 @@
package buffer
import (
"bufio"
"bytes"
"errors"
"io"
@@ -10,11 +11,10 @@ import (
"path/filepath"
"runtime"
"unicode"
"unicode/utf8"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/htmlindex"
"golang.org/x/text/transform"
@@ -55,8 +55,9 @@ func overwriteFile(name string, enc encoding.Encoding, fn func(io.Writer) error,
return
}
w := transform.NewWriter(writeCloser, enc.NewEncoder())
w := bufio.NewWriter(transform.NewWriter(writeCloser, enc.NewEncoder()))
err = fn(w)
w.Flush()
if e := writeCloser.Close(); e != nil && err == nil {
err = e
@@ -98,9 +99,9 @@ func (b *Buffer) saveToFile(filename string, withSudo bool) error {
b.UpdateRules()
if b.Settings["rmtrailingws"].(bool) {
for i, l := range b.lines {
leftover := utf8.RuneCount(bytes.TrimRightFunc(l.data, unicode.IsSpace))
leftover := util.CharacterCount(bytes.TrimRightFunc(l.data, unicode.IsSpace))
linelen := utf8.RuneCount(l.data)
linelen := util.CharacterCount(l.data)
b.Remove(Loc{leftover, i}, Loc{linelen, i})
}
@@ -109,8 +110,8 @@ func (b *Buffer) saveToFile(filename string, withSudo bool) error {
if b.Settings["eofnewline"].(bool) {
end := b.End()
if b.RuneAt(Loc{end.X - 1, end.Y}) != '\n' {
b.Insert(end, "\n")
if b.RuneAt(Loc{end.X, end.Y}) != '\n' {
b.insert(end, []byte{'\n'})
}
}

View File

@@ -2,9 +2,8 @@ package buffer
import (
"regexp"
"unicode/utf8"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/util"
)
func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
@@ -20,19 +19,19 @@ func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
charpos := 0
if i == start.Y && start.Y == end.Y {
nchars := utf8.RuneCount(l)
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
end.X = util.Clamp(end.X, 0, nchars)
l = util.SliceStart(l, end.X)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == start.Y {
nchars := utf8.RuneCount(l)
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == end.Y {
nchars := utf8.RuneCount(l)
nchars := util.CharacterCount(l)
end.X = util.Clamp(end.X, 0, nchars)
l = util.SliceStart(l, end.X)
}
@@ -61,19 +60,19 @@ func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
charpos := 0
if i == start.Y && start.Y == end.Y {
nchars := utf8.RuneCount(l)
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
end.X = util.Clamp(end.X, 0, nchars)
l = util.SliceStart(l, end.X)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == start.Y {
nchars := utf8.RuneCount(l)
nchars := util.CharacterCount(l)
start.X = util.Clamp(start.X, 0, nchars)
l = util.SliceEnd(l, start.X)
charpos = start.X
} else if i == end.Y {
nchars := utf8.RuneCount(l)
nchars := util.CharacterCount(l)
end.X = util.Clamp(end.X, 0, nchars)
l = util.SliceStart(l, end.X)
}
@@ -120,24 +119,27 @@ func (b *Buffer) FindNext(s string, start, end, from Loc, down bool, useRegex bo
if down {
l, found = b.findDown(r, from, end)
if !found {
l, found = b.findDown(r, start, from)
l, found = b.findDown(r, start, end)
}
} else {
l, found = b.findUp(r, from, start)
if !found {
l, found = b.findUp(r, end, from)
l, found = b.findUp(r, end, start)
}
}
return l, found, nil
}
// ReplaceRegex replaces all occurrences of 'search' with 'replace' in the given area
// and returns the number of replacements made
func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte) int {
// and returns the number of replacements made and the number of runes
// added or removed
func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []byte) (int, int) {
if start.GreaterThan(end) {
start, end = end, start
}
netrunes := 0
found := 0
var deltas []Delta
for i := start.Y; i <= end.Y; i++ {
@@ -155,16 +157,21 @@ func (b *Buffer) ReplaceRegex(start, end Loc, search *regexp.Regexp, replace []b
l = util.SliceStart(l, end.X)
}
newText := search.ReplaceAllFunc(l, func(in []byte) []byte {
result := []byte{}
for _, submatches := range search.FindAllSubmatchIndex(in, -1) {
result = search.Expand(result, replace, in, submatches)
}
found++
return replace
netrunes += util.CharacterCount(in) - util.CharacterCount(result)
return result
})
from := Loc{charpos, i}
to := Loc{charpos + utf8.RuneCount(l), i}
to := Loc{charpos + util.CharacterCount(l), i}
deltas = append(deltas, Delta{newText, from, to})
}
b.MultipleReplace(deltas)
return found
return found, netrunes
}

View File

@@ -10,8 +10,8 @@ import (
"golang.org/x/text/encoding"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/util"
)
// The SerializedBuffer holds the types that get serialized when a buffer is saved
@@ -31,7 +31,7 @@ func (b *Buffer) Serialize() error {
return nil
}
name := config.ConfigDir + "/buffers/" + util.EscapePath(b.AbsPath)
name := filepath.Join(config.ConfigDir, "buffers", util.EscapePath(b.AbsPath))
return overwriteFile(name, encoding.Nop, func(file io.Writer) error {
err := gob.NewEncoder(file).Encode(SerializedBuffer{

View File

@@ -1,8 +1,8 @@
package buffer
import (
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
)
func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error {

View File

@@ -151,6 +151,9 @@ func StringToStyle(str string) tcell.Style {
if strings.Contains(str, "bold") {
style = style.Bold(true)
}
if strings.Contains(str, "italic") {
style = style.Italic(true)
}
if strings.Contains(str, "reverse") {
style = style.Reverse(true)
}

View File

@@ -3,6 +3,7 @@ package config
import (
"errors"
"os"
"path/filepath"
homedir "github.com/mitchellh/go-homedir"
)
@@ -24,10 +25,10 @@ func InitConfigDir(flagConfigDir string) error {
if err != nil {
return errors.New("Error finding your home directory\nCan't load config files: " + err.Error())
}
xdgHome = home + "/.config"
xdgHome = filepath.Join(home, ".config")
}
microHome = xdgHome + "/micro"
microHome = filepath.Join(xdgHome, "micro")
}
ConfigDir = microHome

View File

@@ -5,7 +5,7 @@ import (
"log"
lua "github.com/yuin/gopher-lua"
ulua "github.com/zyedidia/micro/internal/lua"
ulua "github.com/zyedidia/micro/v2/internal/lua"
)
// ErrNoSuchFunction is returned when Call is executed on a function that does not exist

View File

@@ -16,8 +16,8 @@ import (
"github.com/blang/semver"
lua "github.com/yuin/gopher-lua"
"github.com/zyedidia/json5"
ulua "github.com/zyedidia/micro/internal/lua"
"github.com/zyedidia/micro/internal/util"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/util"
)
var (

View File

@@ -199,7 +199,6 @@ func InitRuntimeFiles() {
}
p.Info, err = NewPluginInfo(data)
if err != nil {
log.Println(err)
continue
}
p.Name = p.Info.Name
@@ -232,7 +231,6 @@ func InitRuntimeFiles() {
}
p.Info, err = NewPluginInfo(data)
if err != nil {
log.Println(err)
continue
}
p.Name = p.Info.Name

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,7 @@ import (
"github.com/zyedidia/glob"
"github.com/zyedidia/json5"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/util"
"golang.org/x/text/encoding/htmlindex"
)
@@ -147,7 +147,7 @@ func RegisterCommonOptionPlug(pl string, name string, defaultvalue interface{})
if v, ok := GlobalSettings[name]; !ok {
defaultCommonSettings[name] = defaultvalue
GlobalSettings[name] = defaultvalue
err := WriteSettings(filepath.Join(ConfigDir, "/settings.json"))
err := WriteSettings(filepath.Join(ConfigDir, "settings.json"))
if err != nil {
return errors.New("Error writing settings.json file: " + err.Error())
}
@@ -165,14 +165,14 @@ func RegisterGlobalOptionPlug(pl string, name string, defaultvalue interface{})
// RegisterGlobalOption creates a new global-only option
func RegisterGlobalOption(name string, defaultvalue interface{}) error {
if v, ok := GlobalSettings[name]; !ok {
defaultGlobalSettings[name] = defaultvalue
DefaultGlobalOnlySettings[name] = defaultvalue
GlobalSettings[name] = defaultvalue
err := WriteSettings(filepath.Join(ConfigDir, "settings.json"))
if err != nil {
return errors.New("Error writing settings.json file: " + err.Error())
}
} else {
defaultGlobalSettings[name] = v
DefaultGlobalOnlySettings[name] = v
}
return nil
}
@@ -184,13 +184,15 @@ func GetGlobalOption(name string) interface{} {
var defaultCommonSettings = map[string]interface{}{
"autoindent": true,
"autosu": false,
"backup": true,
"basename": false,
"colorcolumn": float64(0),
"cursorline": true,
"diffgutter": false,
"encoding": "utf-8",
"eofnewline": false,
"fastdirty": true,
"eofnewline": true,
"fastdirty": false,
"fileformat": "unix",
"filetype": "unknown",
"ignorecase": false,
@@ -243,7 +245,7 @@ func DefaultCommonSettings() map[string]interface{} {
// a list of settings that should only be globally modified and their
// default values
var defaultGlobalSettings = map[string]interface{}{
var DefaultGlobalOnlySettings = map[string]interface{}{
"autosave": float64(0),
"colorscheme": "default",
"infobar": true,
@@ -254,6 +256,7 @@ var defaultGlobalSettings = map[string]interface{}{
"sucmd": "sudo",
"pluginchannels": []string{"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"},
"pluginrepos": []string{},
"xterm": false,
}
// a list of settings that should never be globally modified
@@ -269,7 +272,7 @@ func DefaultGlobalSettings() map[string]interface{} {
for k, v := range defaultCommonSettings {
globalsettings[k] = v
}
for k, v := range defaultGlobalSettings {
for k, v := range DefaultGlobalOnlySettings {
globalsettings[k] = v
}
return globalsettings
@@ -282,7 +285,7 @@ func DefaultAllSettings() map[string]interface{} {
for k, v := range defaultCommonSettings {
allsettings[k] = v
}
for k, v := range defaultGlobalSettings {
for k, v := range DefaultGlobalOnlySettings {
allsettings[k] = v
}
return allsettings

View File

@@ -2,13 +2,12 @@ package display
import (
"strconv"
"unicode/utf8"
runewidth "github.com/mattn/go-runewidth"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
)
@@ -73,9 +72,9 @@ func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style)
curStyle := config.DefStyle
var s *tcell.Style
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
r, _, size := util.DecodeCharacter(b)
curStyle, found := w.getStyle(curStyle, bloc, r)
curStyle, found := w.getStyle(curStyle, bloc)
if found {
s = &curStyle
}
@@ -136,9 +135,6 @@ func (w *BufWindow) Relocate() bool {
if w.drawStatus {
h--
}
if b.LinesNum() <= h {
height = w.Height
}
ret := false
activeC := w.Buf.GetActiveCursor()
cy := activeC.Y
@@ -212,6 +208,9 @@ func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc {
if hasMessage {
vloc.X += 2
}
if b.Settings["diffgutter"].(bool) {
vloc.X++
}
if b.Settings["ruler"].(bool) {
vloc.X += maxLineNumLength + 1
}
@@ -237,7 +236,7 @@ func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc {
return bloc
}
r, size := utf8.DecodeRune(line)
r, _, size := util.DecodeCharacter(line)
draw()
width := 0
@@ -272,11 +271,7 @@ func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc {
if vloc.Y >= bufHeight {
break
}
vloc.X = 0
// This will draw an empty line number because the current line is wrapped
if b.Settings["ruler"].(bool) {
vloc.X += maxLineNumLength + 1
}
vloc.X = w.gutterOffset
}
}
}
@@ -311,6 +306,34 @@ func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) {
vloc.X++
}
func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) {
symbol := ' '
styleName := ""
switch w.Buf.DiffStatus(bloc.Y) {
case buffer.DSAdded:
symbol = '\u258C' // Left half block
styleName = "diff-added"
case buffer.DSModified:
symbol = '\u258C' // Left half block
styleName = "diff-modified"
case buffer.DSDeletedAbove:
if !softwrapped {
symbol = '\u2594' // Upper one eighth block
styleName = "diff-deleted"
}
}
style := backgroundStyle
if s, ok := config.Colorscheme[styleName]; ok {
foreground, _, _ := s.Decompose()
style = style.Foreground(foreground)
}
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, symbol, nil, style)
vloc.X++
}
func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxLineNumLength int, vloc *buffer.Loc, bloc *buffer.Loc) {
lineNum := strconv.Itoa(bloc.Y + 1)
@@ -336,7 +359,7 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxL
// getStyle returns the highlight style for the given character position
// If there is no change to the current highlight style it just returns that
func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) (tcell.Style, bool) {
func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc) (tcell.Style, bool) {
if group, ok := w.Buf.Match(bloc.Y)[bloc.X]; ok {
s := config.GetColor(group.String())
return s, true
@@ -373,15 +396,22 @@ func (w *BufWindow) displayBuffer() {
bufWidth--
}
if b.Settings["syntax"].(bool) && b.SyntaxDef != nil {
for _, r := range b.Modifications {
final := -1
for i := r.X; i <= r.Y; i++ {
final = util.Max(b.Highlighter.ReHighlightStates(b, i), final)
}
b.Highlighter.HighlightMatches(b, r.X, final+1)
if b.ModifiedThisFrame {
if b.Settings["diffgutter"].(bool) {
b.UpdateDiff(func(synchronous bool) {
// If the diff was updated asynchronously, the outer call to
// displayBuffer might already be completed and we need to
// schedule a redraw in order to display the new diff.
// Note that this cannot lead to an infinite recursion
// because the modifications were cleared above so there won't
// be another call to UpdateDiff when displayBuffer is called
// during the redraw.
if !synchronous {
screen.Redraw()
}
})
}
b.ClearModifications()
b.ModifiedThisFrame = false
}
var matchingBraces []buffer.Loc
@@ -398,12 +428,14 @@ func (w *BufWindow) displayBuffer() {
r := c.RuneUnder(curX)
rl := c.RuneUnder(curX - 1)
if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
mb, left := b.FindMatchingBrace(bp, curLoc)
matchingBraces = append(matchingBraces, mb)
if !left {
matchingBraces = append(matchingBraces, curLoc)
} else {
matchingBraces = append(matchingBraces, curLoc.Move(-1, b))
mb, left, found := b.FindMatchingBrace(bp, curLoc)
if found {
matchingBraces = append(matchingBraces, mb)
if !left {
matchingBraces = append(matchingBraces, curLoc)
} else {
matchingBraces = append(matchingBraces, curLoc.Move(-1, b))
}
}
}
}
@@ -444,18 +476,28 @@ func (w *BufWindow) displayBuffer() {
for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ {
vloc.X = 0
currentLine := false
for _, c := range cursors {
if bloc.Y == c.Y && w.active {
currentLine = true
break
}
}
s := lineNumStyle
if currentLine {
s = curNumStyle
}
if hasMessage {
w.drawGutter(&vloc, &bloc)
}
if b.Settings["diffgutter"].(bool) {
w.drawDiffGutter(s, false, &vloc, &bloc)
}
if b.Settings["ruler"].(bool) {
s := lineNumStyle
for _, c := range cursors {
if bloc.Y == c.Y && w.active {
s = curNumStyle
break
}
}
w.drawLineNum(s, false, maxLineNumLength, &vloc, &bloc)
}
@@ -467,7 +509,7 @@ func (w *BufWindow) displayBuffer() {
}
bloc.X = bslice
draw := func(r rune, style tcell.Style, showcursor bool) {
draw := func(r rune, combc []rune, style tcell.Style, showcursor bool) {
if nColsBeforeStart <= 0 {
for _, c := range cursors {
if c.HasSelection() &&
@@ -513,7 +555,7 @@ func (w *BufWindow) displayBuffer() {
}
if s, ok := config.Colorscheme["color-column"]; ok {
if colorcolumn != 0 && vloc.X-w.gutterOffset == colorcolumn {
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn {
fg, _, _ := s.Decompose()
style = style.Background(fg)
}
@@ -525,7 +567,7 @@ func (w *BufWindow) displayBuffer() {
}
}
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style)
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style)
if showcursor {
for _, c := range cursors {
@@ -541,10 +583,11 @@ func (w *BufWindow) displayBuffer() {
totalwidth := w.StartCol - nColsBeforeStart
for len(line) > 0 {
r, size := utf8.DecodeRune(line)
curStyle, _ = w.getStyle(curStyle, bloc, r)
r, combc, size := util.DecodeCharacter(line)
draw(r, curStyle, true)
curStyle, _ = w.getStyle(curStyle, bloc)
draw(r, combc, curStyle, true)
width := 0
@@ -561,7 +604,7 @@ func (w *BufWindow) displayBuffer() {
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
if width > 1 {
for i := 1; i < width; i++ {
draw(char, curStyle, false)
draw(char, nil, curStyle, false)
}
}
bloc.X++
@@ -579,6 +622,13 @@ func (w *BufWindow) displayBuffer() {
break
}
vloc.X = 0
if hasMessage {
w.drawGutter(&vloc, &bloc)
}
if b.Settings["diffgutter"].(bool) {
w.drawDiffGutter(lineNumStyle, true, &vloc, &bloc)
}
// This will draw an empty line number because the current line is wrapped
if b.Settings["ruler"].(bool) {
w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc)
@@ -600,7 +650,7 @@ func (w *BufWindow) displayBuffer() {
for i := vloc.X; i < bufWidth; i++ {
curStyle := style
if s, ok := config.Colorscheme["color-column"]; ok {
if colorcolumn != 0 && i-w.gutterOffset == colorcolumn {
if colorcolumn != 0 && i-w.gutterOffset+w.StartCol == colorcolumn {
fg, _, _ := s.Decompose()
curStyle = style.Background(fg)
}
@@ -609,7 +659,7 @@ func (w *BufWindow) displayBuffer() {
}
if vloc.X != bufWidth {
draw(' ', curStyle, true)
draw(' ', nil, curStyle, true)
}
bloc.X = w.StartCol

View File

@@ -1,14 +1,12 @@
package display
import (
"unicode/utf8"
runewidth "github.com/mattn/go-runewidth"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/info"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/info"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
)
@@ -70,7 +68,7 @@ func (i *InfoWindow) IsActive() bool { return true }
func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc {
c := i.Buffer.GetActiveCursor()
l := i.Buffer.LineBytes(0)
n := utf8.RuneCountInString(i.Msg)
n := util.CharacterCountInString(i.Msg)
return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0}
}
@@ -86,13 +84,13 @@ func (i *InfoWindow) displayBuffer() {
activeC := b.GetActiveCursor()
blocX := 0
vlocX := utf8.RuneCountInString(i.Msg)
vlocX := util.CharacterCountInString(i.Msg)
tabsize := 4
line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, blocX, tabsize)
blocX = bslice
draw := func(r rune, style tcell.Style) {
draw := func(r rune, combc []rune, style tcell.Style) {
if nColsBeforeStart <= 0 {
bloc := buffer.Loc{X: blocX, Y: 0}
if activeC.HasSelection() &&
@@ -112,8 +110,9 @@ func (i *InfoWindow) displayBuffer() {
c := r
if j > 0 {
c = ' '
combc = nil
}
screen.SetContent(vlocX, i.Y, c, nil, style)
screen.SetContent(vlocX, i.Y, c, combc, style)
}
vlocX++
}
@@ -122,13 +121,11 @@ func (i *InfoWindow) displayBuffer() {
totalwidth := blocX - nColsBeforeStart
for len(line) > 0 {
if activeC.X == blocX {
screen.ShowCursor(vlocX, i.Y)
}
curVX := vlocX
curBX := blocX
r, combc, size := util.DecodeCharacter(line)
r, size := utf8.DecodeRune(line)
draw(r, i.defStyle())
draw(r, combc, i.defStyle())
width := 0
@@ -148,9 +145,12 @@ func (i *InfoWindow) displayBuffer() {
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
if width > 1 {
for j := 1; j < width; j++ {
draw(char, i.defStyle())
draw(char, nil, i.defStyle())
}
}
if activeC.X == curBX {
screen.ShowCursor(curVX, i.Y)
}
totalwidth += width
if vlocX >= i.Width {
break
@@ -190,7 +190,7 @@ func (i *InfoWindow) scrollToSuggestion() {
s := i.totalSize()
for j, n := range i.Suggestions {
c := utf8.RuneCountInString(n)
c := util.CharacterCountInString(n)
if j == i.CurSuggestion {
if x+c >= i.hscroll+i.Width {
i.hscroll = util.Clamp(x+c+1-i.Width, 0, s-i.Width)
@@ -208,12 +208,13 @@ func (i *InfoWindow) scrollToSuggestion() {
}
func (i *InfoWindow) Display() {
x := 0
if config.GetGlobalOption("keymenu").(bool) {
i.displayKeyMenu()
}
if i.HasPrompt || config.GlobalSettings["infobar"].(bool) {
i.Clear()
x := 0
if config.GetGlobalOption("keymenu").(bool) {
i.displayKeyMenu()
}
if !i.HasPrompt && !i.HasMessage && !i.HasError {
return
}

View File

@@ -3,21 +3,19 @@ package display
import (
"bytes"
"fmt"
"path"
"regexp"
"strconv"
"strings"
"unicode/utf8"
luar "layeh.com/gopher-luar"
runewidth "github.com/mattn/go-runewidth"
lua "github.com/yuin/gopher-lua"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
ulua "github.com/zyedidia/micro/internal/lua"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
)
// StatusLine represents the information line at the bottom
@@ -32,9 +30,6 @@ type StatusLine struct {
var statusInfo = map[string]func(*buffer.Buffer) string{
"filename": func(b *buffer.Buffer) string {
if b.Settings["basename"].(bool) {
return path.Base(b.GetName())
}
return b.GetName()
},
"line": func(b *buffer.Buffer) string {
@@ -172,34 +167,36 @@ func (s *StatusLine) Display() {
statusLineStyle = style
}
leftLen := util.StringWidth(leftText, utf8.RuneCount(leftText), 1)
rightLen := util.StringWidth(rightText, utf8.RuneCount(rightText), 1)
leftLen := util.StringWidth(leftText, util.CharacterCount(leftText), 1)
rightLen := util.StringWidth(rightText, util.CharacterCount(rightText), 1)
winX := s.win.X
for x := 0; x < s.win.Width; x++ {
if x < leftLen {
r, size := utf8.DecodeRune(leftText)
r, combc, size := util.DecodeCharacter(leftText)
leftText = leftText[size:]
rw := runewidth.RuneWidth(r)
for j := 0; j < rw; j++ {
c := r
if j > 0 {
c = ' '
combc = nil
x++
}
screen.SetContent(winX+x, y, c, nil, statusLineStyle)
screen.SetContent(winX+x, y, c, combc, statusLineStyle)
}
} else if x >= s.win.Width-rightLen && x < rightLen+s.win.Width-rightLen {
r, size := utf8.DecodeRune(rightText)
r, combc, size := util.DecodeCharacter(rightText)
rightText = rightText[size:]
rw := runewidth.RuneWidth(r)
for j := 0; j < rw; j++ {
c := r
if j > 0 {
c = ' '
combc = nil
x++
}
screen.SetContent(winX+x, y, c, nil, statusLineStyle)
screen.SetContent(winX+x, y, c, combc, statusLineStyle)
}
} else {
screen.SetContent(winX+x, y, ' ', nil, statusLineStyle)

View File

@@ -1,32 +1,30 @@
package display
import (
"unicode/utf8"
runewidth "github.com/mattn/go-runewidth"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
)
type TabWindow struct {
Names []string
active int
Y int
width int
Width int
hscroll int
}
func NewTabWindow(w int, y int) *TabWindow {
tw := new(TabWindow)
tw.width = w
tw.Width = w
tw.Y = y
return tw
}
func (w *TabWindow) Resize(width, height int) {
w.width = width
w.Width = width
}
func (w *TabWindow) LocFromVisual(vloc buffer.Loc) int {
@@ -34,13 +32,13 @@ func (w *TabWindow) LocFromVisual(vloc buffer.Loc) int {
for i, n := range w.Names {
x++
s := utf8.RuneCountInString(n)
s := util.CharacterCountInString(n)
if vloc.Y == w.Y && vloc.X < x+s {
return i
}
x += s
x += 3
if x >= w.width {
if x >= w.Width {
break
}
}
@@ -50,9 +48,9 @@ func (w *TabWindow) LocFromVisual(vloc buffer.Loc) int {
func (w *TabWindow) Scroll(amt int) {
w.hscroll += amt
s := w.TotalSize()
w.hscroll = util.Clamp(w.hscroll, 0, s-w.width)
w.hscroll = util.Clamp(w.hscroll, 0, s-w.Width)
if s-w.width <= 0 {
if s-w.Width <= 0 {
w.hscroll = 0
}
}
@@ -75,19 +73,19 @@ func (w *TabWindow) SetActive(a int) {
s := w.TotalSize()
for i, n := range w.Names {
c := utf8.RuneCountInString(n)
c := util.CharacterCountInString(n)
if i == a {
if x+c >= w.hscroll+w.width {
w.hscroll = util.Clamp(x+c+1-w.width, 0, s-w.width)
if x+c >= w.hscroll+w.Width {
w.hscroll = util.Clamp(x+c+1-w.Width, 0, s-w.Width)
} else if x < w.hscroll {
w.hscroll = util.Clamp(x-4, 0, s-w.width)
w.hscroll = util.Clamp(x-4, 0, s-w.Width)
}
break
}
x += c + 4
}
if s-w.width <= 0 {
if s-w.Width <= 0 {
w.hscroll = 0
}
}
@@ -96,6 +94,11 @@ func (w *TabWindow) Display() {
x := -w.hscroll
done := false
tabBarStyle := config.DefStyle.Reverse(true)
if style, ok := config.Colorscheme["tabbar"]; ok {
tabBarStyle = style
}
draw := func(r rune, n int) {
for i := 0; i < n; i++ {
rw := runewidth.RuneWidth(r)
@@ -104,14 +107,14 @@ func (w *TabWindow) Display() {
if j > 0 {
c = ' '
}
if x == w.width-1 && !done {
screen.SetContent(w.width-1, w.Y, '>', nil, config.DefStyle.Reverse(true))
if x == w.Width-1 && !done {
screen.SetContent(w.Width-1, w.Y, '>', nil, tabBarStyle)
x++
break
} else if x == 0 && w.hscroll > 0 {
screen.SetContent(0, w.Y, '<', nil, config.DefStyle.Reverse(true))
} else if x >= 0 && x < w.width {
screen.SetContent(x, w.Y, c, nil, config.DefStyle.Reverse(true))
screen.SetContent(0, w.Y, '<', nil, tabBarStyle)
} else if x >= 0 && x < w.Width {
screen.SetContent(x, w.Y, c, nil, tabBarStyle)
}
x++
}
@@ -136,12 +139,12 @@ func (w *TabWindow) Display() {
} else {
draw(' ', 3)
}
if x >= w.width {
if x >= w.Width {
break
}
}
if x < w.width {
draw(' ', w.width-x)
if x < w.Width {
draw(' ', w.Width-x)
}
}

View File

@@ -1,12 +1,11 @@
package display
import (
"unicode/utf8"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/shell"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/terminal"
)
@@ -98,12 +97,12 @@ func (w *TermWindow) Display() {
}
text := []byte(w.Name())
textLen := utf8.RuneCount(text)
textLen := util.CharacterCount(text)
for x := 0; x < w.Width; x++ {
if x < textLen {
r, size := utf8.DecodeRune(text)
r, combc, size := util.DecodeCharacter(text)
text = text[size:]
screen.SetContent(w.X+x, w.Y+w.Height, r, nil, statusLineStyle)
screen.SetContent(w.X+x, w.Y+w.Height, r, combc, statusLineStyle)
} else {
screen.SetContent(w.X+x, w.Y+w.Height, ' ', nil, statusLineStyle)
}

View File

@@ -1,10 +1,10 @@
package display
import (
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/views"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/views"
)
type UIWindow struct {

View File

@@ -1,7 +1,7 @@
package display
import (
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/v2/internal/buffer"
)
type View struct {

View File

@@ -3,8 +3,9 @@ package info
import (
"encoding/gob"
"os"
"path/filepath"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/v2/internal/config"
)
// LoadHistory attempts to load user history from configDir/buffers/history
@@ -12,7 +13,7 @@ import (
// The savehistory option must be on
func (i *InfoBuf) LoadHistory() {
if config.GetGlobalOption("savehistory").(bool) {
file, err := os.Open(config.ConfigDir + "/buffers/history")
file, err := os.Open(filepath.Join(config.ConfigDir, "buffers", "history"))
defer file.Close()
var decodedMap map[string][]string
if err == nil {
@@ -46,7 +47,7 @@ func (i *InfoBuf) SaveHistory() {
}
}
file, err := os.Create(config.ConfigDir + "/buffers/history")
file, err := os.Create(filepath.Join(config.ConfigDir, "buffers", "history"))
defer file.Close()
if err == nil {
encoder := gob.NewEncoder(file)

View File

@@ -3,7 +3,7 @@ package info
import (
"fmt"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/v2/internal/buffer"
)
// The InfoBuf displays messages and other info at the bottom of the screen.

View File

@@ -5,8 +5,8 @@ import (
"os"
"sync"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
)
@@ -21,9 +21,9 @@ var Screen tcell.Screen
// The lock is necessary since the screen is polled on a separate thread
var lock sync.Mutex
// DrawChan is a channel that will cause the screen to redraw when
// drawChan is a channel that will cause the screen to redraw when
// written to even if no event user event has occurred
var DrawChan chan bool
var drawChan chan bool
// Lock locks the screen lock
func Lock() {
@@ -37,7 +37,16 @@ func Unlock() {
// Redraw schedules a redraw with the draw channel
func Redraw() {
DrawChan <- true
select {
case drawChan <- true:
default:
// channel is full
}
}
// DrawChan returns the draw channel
func DrawChan() chan bool {
return drawChan
}
type screenCell struct {
@@ -118,7 +127,7 @@ func TempStart(screenWasNil bool) {
// Init creates and initializes the tcell screen
func Init() {
DrawChan = make(chan bool, 8)
drawChan = make(chan bool)
// Should we enable true color?
truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
@@ -127,6 +136,12 @@ func Init() {
os.Setenv("TCELL_TRUECOLOR", "disable")
}
var oldTerm string
if config.GetGlobalOption("xterm").(bool) {
oldTerm = os.Getenv("TERM")
os.Setenv("TERM", "xterm-256color")
}
// Initilize tcell
var err error
Screen, err = tcell.NewScreen()
@@ -140,6 +155,11 @@ func Init() {
os.Exit(1)
}
// restore TERM
if config.GetGlobalOption("xterm").(bool) {
os.Setenv("TERM", oldTerm)
}
if config.GetGlobalOption("mouse").(bool) {
Screen.EnableMouse()
}

View File

@@ -11,7 +11,7 @@ import (
"strings"
shellquote "github.com/kballard/go-shellquote"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/v2/internal/screen"
)
// ExecCommand executes a command using exec

View File

@@ -5,8 +5,8 @@ import (
"os/exec"
"strconv"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/terminal"
)

View File

@@ -1,15 +1,11 @@
package util
import (
"unicode/utf8"
)
// LuaRuneAt is a helper function for lua plugins to return the rune
// at an index within a string
func LuaRuneAt(str string, runeidx int) string {
i := 0
for len(str) > 0 {
r, size := utf8.DecodeRuneInString(str)
r, _, size := DecodeCharacterInString(str)
str = str[size:]
@@ -26,7 +22,7 @@ func LuaRuneAt(str string, runeidx int) string {
func LuaGetLeadingWhitespace(s string) string {
ws := []byte{}
for len(s) > 0 {
r, size := utf8.DecodeRuneInString(s)
r, _, size := DecodeCharacterInString(s)
if r == ' ' || r == '\t' {
ws = append(ws, byte(r))
} else {
@@ -40,6 +36,6 @@ func LuaGetLeadingWhitespace(s string) string {
// LuaIsWordChar returns true if the first rune in a string is a word character
func LuaIsWordChar(s string) bool {
r, _ := utf8.DecodeRuneInString(s)
r, _, _ := DecodeCharacterInString(s)
return IsWordChar(r)
}

86
internal/util/unicode.go Normal file
View File

@@ -0,0 +1,86 @@
package util
import (
"unicode"
"unicode/utf8"
)
// Unicode is annoying. A "code point" (rune in Go-speak) may need up to
// 4 bytes to represent it. In general, a code point will represent a
// complete character, but this is not always the case. A character with
// accents may be made up of multiple code points (the code point for the
// original character, and additional code points for each accent/marking).
// The functions below are meant to help deal with these additional "combining"
// code points. In underlying operations (search, replace, etc...), micro will
// treat a character with combining code points as just the original code point.
// For rendering, micro will display the combining characters. It's not perfect
// but it's pretty good.
// DecodeCharacter returns the next character from an array of bytes
// A character is a rune along with any accompanying combining runes
func DecodeCharacter(b []byte) (rune, []rune, int) {
r, size := utf8.DecodeRune(b)
b = b[size:]
c, s := utf8.DecodeRune(b)
var combc []rune
for unicode.In(c, unicode.Mark) {
combc = append(combc, c)
size += s
b = b[s:]
c, s = utf8.DecodeRune(b)
}
return r, combc, size
}
// DecodeCharacterInString returns the next character from a string
// A character is a rune along with any accompanying combining runes
func DecodeCharacterInString(str string) (rune, []rune, int) {
r, size := utf8.DecodeRuneInString(str)
str = str[size:]
c, s := utf8.DecodeRuneInString(str)
var combc []rune
for unicode.In(c, unicode.Mark) {
combc = append(combc, c)
size += s
str = str[s:]
c, s = utf8.DecodeRuneInString(str)
}
return r, combc, size
}
// CharacterCount returns the number of characters in a byte array
// Similar to utf8.RuneCount but for unicode characters
func CharacterCount(b []byte) int {
s := 0
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
if !unicode.In(r, unicode.Mark) {
s++
}
b = b[size:]
}
return s
}
// CharacterCount returns the number of characters in a string
// Similar to utf8.RuneCountInString but for unicode characters
func CharacterCountInString(str string) int {
s := 0
for _, r := range str {
if !unicode.In(r, unicode.Mark) {
s++
}
}
return s
}

View File

@@ -1,6 +1,7 @@
package util
import (
"bytes"
"errors"
"fmt"
"os"
@@ -12,7 +13,6 @@ import (
"strings"
"time"
"unicode"
"unicode/utf8"
"github.com/blang/semver"
runewidth "github.com/mattn/go-runewidth"
@@ -30,10 +30,13 @@ var (
// CompileDate is the date this binary was compiled on
CompileDate = "Unknown"
// Debug logging
Debug = "ON"
Debug = "OFF"
// FakeCursor is used to disable the terminal cursor and have micro
// draw its own (enabled for windows consoles where the cursor is slow)
FakeCursor = false
// Stdout is a buffer that is written to stdout when micro closes
Stdout *bytes.Buffer
)
func init() {
@@ -46,6 +49,7 @@ func init() {
if runtime.GOOS == "windows" {
FakeCursor = true
}
Stdout = new(bytes.Buffer)
}
// SliceEnd returns a byte slice where the index is a rune index
@@ -59,7 +63,7 @@ func SliceEnd(slc []byte, index int) []byte {
return slc[totalSize:]
}
_, size := utf8.DecodeRune(slc[totalSize:])
_, _, size := DecodeCharacter(slc[totalSize:])
totalSize += size
i++
}
@@ -77,7 +81,7 @@ func SliceEndStr(str string, index int) string {
return str[totalSize:]
}
_, size := utf8.DecodeRuneInString(str[totalSize:])
_, _, size := DecodeCharacterInString(str[totalSize:])
totalSize += size
i++
}
@@ -96,7 +100,7 @@ func SliceStart(slc []byte, index int) []byte {
return slc[:totalSize]
}
_, size := utf8.DecodeRune(slc[totalSize:])
_, _, size := DecodeCharacter(slc[totalSize:])
totalSize += size
i++
}
@@ -114,7 +118,7 @@ func SliceStartStr(str string, index int) string {
return str[:totalSize]
}
_, size := utf8.DecodeRuneInString(str[totalSize:])
_, _, size := DecodeCharacterInString(str[totalSize:])
totalSize += size
i++
}
@@ -130,7 +134,7 @@ func SliceVisualEnd(b []byte, n, tabsize int) ([]byte, int, int) {
width := 0
i := 0
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
r, _, size := DecodeCharacter(b)
w := 0
switch r {
@@ -167,7 +171,7 @@ func StringWidth(b []byte, n, tabsize int) int {
i := 0
width := 0
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
r, _, size := DecodeCharacter(b)
b = b[size:]
switch r {
@@ -260,7 +264,7 @@ func IsBytesWhitespace(b []byte) bool {
// RunePos returns the rune index of a given byte index
// Make sure the byte index is not between code points
func RunePos(b []byte, i int) int {
return utf8.RuneCount(b[:i])
return CharacterCount(b[:i])
}
// MakeRelative will attempt to make a relative path between path and base
@@ -339,7 +343,7 @@ func EscapePath(path string) string {
func GetLeadingWhitespace(b []byte) []byte {
ws := []byte{}
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
r, _, size := DecodeCharacter(b)
if r == ' ' || r == '\t' {
ws = append(ws, byte(r))
} else {
@@ -365,7 +369,7 @@ func GetCharPosInLine(b []byte, visualPos int, tabsize int) int {
i := 0 // char pos
width := 0 // string visual width
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
r, _, size := DecodeCharacter(b)
b = b[size:]
switch r {
@@ -411,7 +415,7 @@ func Clamp(val, min, max int) int {
}
func IsNonAlphaNumeric(c rune) bool {
return !unicode.IsLetter(c) && !unicode.IsNumber(c)
return !unicode.IsLetter(c) && !unicode.IsNumber(c) && c != '_'
}
func ParseSpecial(s string) string {

View File

@@ -3,7 +3,6 @@ package highlight
import (
"regexp"
"strings"
"unicode/utf8"
)
func sliceStart(slc []byte, index int) []byte {
@@ -15,7 +14,7 @@ func sliceStart(slc []byte, index int) []byte {
return slc[totalSize:]
}
_, size := utf8.DecodeRune(slc[totalSize:])
_, _, size := DecodeCharacter(slc[totalSize:])
totalSize += size
i++
}
@@ -32,7 +31,7 @@ func sliceEnd(slc []byte, index int) []byte {
return slc[:totalSize]
}
_, size := utf8.DecodeRune(slc[totalSize:])
_, _, size := DecodeCharacter(slc[totalSize:])
totalSize += size
i++
}
@@ -47,9 +46,9 @@ func runePos(p int, str []byte) int {
return 0
}
if p >= len(str) {
return utf8.RuneCount(str)
return CharacterCount(str)
}
return utf8.RuneCount(str[:p])
return CharacterCount(str[:p])
}
func combineLineMatch(src, dst LineMatch) LineMatch {
@@ -112,7 +111,7 @@ func findIndex(regex *regexp.Regexp, skip *regexp.Regexp, str []byte, canMatchSt
var strbytes []byte
if skip != nil {
strbytes = skip.ReplaceAllFunc(str, func(match []byte) []byte {
res := make([]byte, utf8.RuneCount(match))
res := make([]byte, CharacterCount(match))
return res
})
} else {
@@ -148,7 +147,7 @@ func findAllIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd b
}
func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchEnd bool, lineNum int, line []byte, curRegion *region, statesOnly bool) LineMatch {
lineLen := utf8.RuneCount(line)
lineLen := CharacterCount(line)
if start == 0 {
if !statesOnly {
if _, ok := highlights[0]; !ok {
@@ -236,7 +235,7 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE
}
func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canMatchEnd bool, lineNum int, line []byte, statesOnly bool) LineMatch {
lineLen := utf8.RuneCount(line)
lineLen := CharacterCount(line)
if lineLen == 0 {
if canMatchEnd {
h.lastRegion = nil
@@ -336,11 +335,11 @@ func (h *Highlighter) HighlightStates(input LineStates) {
}
}
// HighlightMatches sets the matches for each line in between startline and endline
// HighlightMatches sets the matches for each line from startline to endline
// It sets all other matches in the buffer to nil to conserve memory
// This assumes that all the states are set correctly
func (h *Highlighter) HighlightMatches(input LineStates, startline, endline int) {
for i := startline; i < endline; i++ {
for i := startline; i <= endline; i++ {
if i >= input.LinesNum() {
break
}

75
pkg/highlight/unicode.go Normal file
View File

@@ -0,0 +1,75 @@
package highlight
import (
"unicode"
"unicode/utf8"
)
// DecodeCharacter returns the next character from an array of bytes
// A character is a rune along with any accompanying combining runes
func DecodeCharacter(b []byte) (rune, []rune, int) {
r, size := utf8.DecodeRune(b)
b = b[size:]
c, s := utf8.DecodeRune(b)
var combc []rune
for unicode.In(c, unicode.Mark) {
combc = append(combc, c)
size += s
b = b[s:]
c, s = utf8.DecodeRune(b)
}
return r, combc, size
}
// DecodeCharacterInString returns the next character from a string
// A character is a rune along with any accompanying combining runes
func DecodeCharacterInString(str string) (rune, []rune, int) {
r, size := utf8.DecodeRuneInString(str)
str = str[size:]
c, s := utf8.DecodeRuneInString(str)
var combc []rune
for unicode.In(c, unicode.Mark) {
combc = append(combc, c)
size += s
str = str[s:]
c, s = utf8.DecodeRuneInString(str)
}
return r, combc, size
}
// CharacterCount returns the number of characters in a byte array
// Similar to utf8.RuneCount but for unicode characters
func CharacterCount(b []byte) int {
s := 0
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
if !unicode.In(r, unicode.Mark) {
s++
}
b = b[size:]
}
return s
}
// CharacterCount returns the number of characters in a string
// Similar to utf8.RuneCountInString but for unicode characters
func CharacterCountInString(str string) int {
s := 0
for _, r := range str {
if !unicode.In(r, unicode.Mark) {
s++
}
}
return s
}

View File

@@ -16,6 +16,9 @@ color-link tabbar "#1D1F21,#C5C8C6"
color-link indent-char "#505050,#1D1F21"
color-link line-number "#656866,#232526"
color-link current-line-number "#656866,#1D1F21"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#FF4444,#1D1F21"
color-link gutter-warning "#EEEE77,#1D1F21"
color-link cursor-line "#2D2F31"

View File

@@ -14,6 +14,9 @@ color-link underlined "underline 241,231"
color-link todo "246,231"
color-link statusline "241,254"
color-link tabbar "241,254"
color-link diff-added "34"
color-link diff-modified "214"
color-link diff-deleted "160"
color-link gutter-error "197,231"
color-link gutter-warning "134,231"
color-link line-number "246,254"

View File

@@ -33,6 +33,9 @@ color-link statusline "white,blue"
color-link tabbar "white,blue"
color-link current-line-number "red"
color-link current-line-number.scroller "red"
color-link diff-added "green"
color-link diff-modified "yellow"
color-link diff-deleted "red"
color-link gutter-error ",red"
color-link gutter-warning "red"
color-link color-column "cyan"

View File

@@ -28,9 +28,12 @@ color-link statusline "#aaaaaa,#8a496b"
color-link tabbar "#aaaaaa,#8a496b"
color-link current-line-number "bold #e34234,#424549"
color-link current-line-number.scroller "red"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error ",#e34234"
color-link gutter-warning "#e34234"
color-link color-column "#f26522"
color-link constant.bool "bold #55ffff"
color-link constant.bool.true "bold #85ff85"
color-link constant.bool.false "bold #ff8585"
color-link constant.bool.false "bold #ff8585"

View File

@@ -17,6 +17,9 @@ color-link tabbar "#242424,#CCCCCC"
color-link indent-char "#4F4F4F,#242424"
color-link line-number "#666666,#2C2C2C"
color-link current-line-number "#666666,#242424"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#CB4B16,#242424"
color-link gutter-warning "#E6DB74,#242424"
color-link cursor-line "#2C2C2C"

View File

@@ -17,6 +17,9 @@ color-link tabbar "#282828,#F8F8F2"
color-link indent-char "#505050,#282828"
color-link line-number "#AAAAAA,#323232"
color-link current-line-number "#AAAAAA,#282828"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#CB4B16,#282828"
color-link gutter-warning "#E6DB74,#282828"
color-link cursor-line "#323232"

View File

@@ -0,0 +1,34 @@
color-link color-column "#001e28"
color-link comment "#608b4e,#001e28"
color-link constant.bool "#fd971f,#001e28"
color-link constant "#fd971f,#001e28"
color-link constant.string "#a0f000,#001e28"
color-link constant.string.char "#a0f000,#001e28"
color-link constant.string.url "#a0f000,#001e28"
color-link current-line-number "bold #fd971f,#001e28"
color-link cursor-line "#001923"
color-link default "#ffffff,#001e28"
color-link diff-added "#00c8a0,#001e28"
color-link diff-modified "#fd971f,#001e28"
color-link diff-deleted "#cb4b16,#001e28"
color-link divider "#001e28,#d0d0d0"
color-link error "#cb4b16,#001e28"
color-link gutter-error "#cb4b16,#001e28"
color-link gutter-warning "#fce94f,#001e28"
color-link identifier "#00c8a0,#001e28"
color-link identifier.class "#00c8a0,#001e28"
color-link indent-char "#a0a0a0,#001e28"
color-link line-number "#a0a0a0,#001923"
color-link preproc "bold #5aaae6,#001e28"
color-link special "#a6e22e,#001e28"
color-link statement "bold #5aaae6,#001e28"
color-link statusline "#ffffff,#0078c8"
color-link symbol "#00c8a0,#001e28"
color-link symbol.brackets "#ffffff,#001e28"
color-link symbol.tag "bold #5aaae6,#001e28"
color-link tabbar "#001e28,#ffffff"
color-link todo "#fce94f,#001e28"
color-link type "bold #3cc83c,#001e28"
color-link type.keyword "bold #5aaae6,#001e28"
color-link type.extended "#ffffff,#001e28"
color-link underlined "#608b4e,#001e28"

View File

@@ -0,0 +1,34 @@
color-link color-column "#f0f0f0"
color-link comment "#3f7f5f,#f0f0f0"
color-link constant.bool "#641e00,#f0f0f0"
color-link constant "#641e00,#f0f0f0"
color-link constant.string "#0000ff,#f0f0f0"
color-link constant.string.char "#0000ff,#f0f0f0"
color-link constant.string.url "#0000ff,#f0f0f0"
color-link current-line-number "bold #004080,#f0f0f0"
color-link cursor-line "#e6e6e6"
color-link default "#000000,#f0f0f0"
color-link diff-added "#008040,#f0f0f0"
color-link diff-modified "#641e00,#f0f0f0"
color-link diff-deleted "#500000,#f0f0f0"
color-link divider "#f0f0f0,#004080"
color-link error "#500000,#f0f0f0"
color-link gutter-error "#500000,#f0f0f0"
color-link gutter-warning "#dcc800,#f0f0f0"
color-link identifier "bold #0078a0,#f0f0f0"
color-link identifier.class "bold #0078a0,#f0f0f0"
color-link indent-char "#404040,#f0f0f0"
color-link line-number "#404040,#e6e6e6"
color-link preproc "bold #780050,#f0f0f0"
color-link special "bold #0078a0,#f0f0f0"
color-link statement "bold #780050,#f0f0f0"
color-link statusline "#ffffff,#0078c8"
color-link symbol "bold #0078a0,#f0f0f0"
color-link symbol.brackets "#000000,#f0f0f0"
color-link symbol.tag "bold #780050,#f0f0f0"
color-link tabbar "#f0f0f0,#004080"
color-link todo "#dcc800,#f0f0f0"
color-link type "bold #004080,#f0f0f0"
color-link type.keyword "bold #780050,#f0f0f0"
color-link type.extended "#000000,#f0f0f0"
color-link underlined "#3f7f5f,#f0f0f0"

View File

@@ -0,0 +1,34 @@
color-link color-column "#2d0023"
color-link comment "#886484,#2d0023"
color-link constant.bool "#fd971f,#2d0023"
color-link constant "#fd971f,#2d0023"
color-link constant.string "#a0f000,#2d0023"
color-link constant.string.char "#a0f000,#2d0023"
color-link constant.string.url "#a0f000,#2d0023"
color-link current-line-number "bold #fd971f,#2d0023"
color-link cursor-line "#230019"
color-link default "#ffffff,#2d0023"
color-link diff-added "#00c8a0,#2d0023"
color-link diff-modified "#fd971f,#2d0023"
color-link diff-deleted "#cb4b16,#2d0023"
color-link divider "#2d0023,#d0d0d0"
color-link error "#cb4b16,#2d0023"
color-link gutter-error "#cb4b16,#2d0023"
color-link gutter-warning "#fce94f,#2d0023"
color-link identifier "#00c8a0,#2d0023"
color-link identifier.class "#00c8a0,#2d0023"
color-link indent-char "#a0a0a0,#2d0023"
color-link line-number "#a0a0a0,#230019"
color-link preproc "bold #5aaae6,#2d0023"
color-link special "#a6e22e,#2d0023"
color-link statement "bold #5aaae6,#2d0023"
color-link statusline "#ffffff,#0078c8"
color-link symbol "#00c8a0,#2d0023"
color-link symbol.brackets "#ffffff,#2d0023"
color-link symbol.tag "bold #5aaae6,#2d0023"
color-link tabbar "#2d0023,#ffffff"
color-link todo "#fce94f,#2d0023"
color-link type "bold #3cc83c,#2d0023"
color-link type.keyword "bold #5aaae6,#2d0023"
color-link type.extended "#ffffff,#2d0023"
color-link underlined "#886484,#2d0023"

View File

@@ -18,5 +18,8 @@ color-link current-line-number ""
color-link statusline "black,white"
color-link tabbar "black,white"
color-link color-column "bold geren"
color-link diff-added "green"
color-link diff-modified "yellow"
color-link diff-deleted "red"
color-link gutter-error ",red"
color-link gutter-warning "red"

View File

@@ -15,6 +15,9 @@ color-link statusline "#091F2E,#599CAB"
color-link indent-char "#505050,#0C1014"
color-link line-number "#245361,#11151C"
color-link current-line-number "#599CAB,#11151C"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#C23127,#11151C"
color-link gutter-warning "#EDB443,#11151C"
color-link cursor-line "#091F2E"

View File

@@ -11,6 +11,9 @@ color-link type "#fb4934,#282828"
color-link special "#d79921,#282828"
color-link underlined "underline #282828"
color-link error "#9d0006,#282828"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#fb4934,#282828"
color-link gutter-warning "#d79921,#282828"
color-link line-number "#665c54,#3c3836"

View File

@@ -11,6 +11,9 @@ color-link special "172,235"
color-link underlined "underline 109,235"
color-link error "235,124"
color-link todo "bold 223,235"
color-link diff-added "34"
color-link diff-modified "214"
color-link diff-deleted "160"
color-link line-number "243,237"
color-link current-line-number "172,235"
color-link cursor-line "237"

View File

@@ -7,6 +7,9 @@ color-link constant.string "#C3E88D,#263238"
color-link current-line-number "#80DEEA,#263238"
color-link cursor-line "#283942"
color-link default "#EEFFFF,#263238"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link divider "#263238,#80DEEA"
color-link error "bold #263238,#F07178"
color-link gutter-error "#EEFFFF,#F07178"

View File

@@ -15,6 +15,9 @@ color-link statusline "#282828,#F8F8F2"
color-link indent-char "#505050,#282828"
color-link line-number "#AAAAAA,#282828"
color-link current-line-number "#AAAAAA,#1D0000"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#CB4B16"
color-link gutter-warning "#E6DB74"
color-link cursor-line "#323232"

View File

@@ -17,6 +17,9 @@ color-link tabbar "#282828,#F8F8F2"
color-link indent-char "#505050,#282828"
color-link line-number "#AAAAAA,#323232"
color-link current-line-number "#AAAAAA,#282828"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#CB4B16,#282828"
color-link gutter-warning "#E6DB74,#282828"
color-link cursor-line "#323232"

View File

@@ -8,8 +8,11 @@ color-link constant.string.char "#BDE6AD"
color-link constant.specialChar "#DDF2A4"
color-link current-line-number "#C6C6C6,#21252C"
color-link cursor-line "#282C34"
color-link divider "#1E1E1E"
color-link divider "#ABB2BF"
color-link error "#D2A8A1"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#9B859D"
color-link gutter-warning "#9B859D"
color-link identifier "#61AFEF"

View File

@@ -13,6 +13,9 @@ color-link gutter-error "#cc7833,#11151C"
color-link indent-char "#414141,#2b2b2b"
color-link line-number "#a1a1a1,#353535"
color-link current-line-number "#e6e1dc,#2b2b2b"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-warning "#a5c261,#11151C"
color-link symbol "#edb753,#2b2b2b"
color-link identifier "#edb753,#2b2b2b"

View File

@@ -12,6 +12,9 @@ color-link todo ",brightyellow"
color-link indent-char "black"
color-link line-number "yellow"
color-link current-line-number "red"
color-link diff-added "green"
color-link diff-modified "yellow"
color-link diff-deleted "red"
color-link gutter-error ",red"
color-link gutter-warning "red"
#Cursor line causes readability issues. Disabled for now.

View File

@@ -16,6 +16,9 @@ color-link tabbar "#003541,#839496"
color-link indent-char "#003541,#002833"
color-link line-number "#586E75,#003541"
color-link current-line-number "#586E75,#002833"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#003541,#CB4B16"
color-link gutter-warning "#CB4B16,#002833"
color-link cursor-line "#003541"

View File

@@ -15,6 +15,9 @@ color-link tabbar "black,brightblue"
color-link indent-char "black"
color-link line-number "bold brightgreen,black"
color-link current-line-number "bold brightgreen,default"
color-link diff-added "green"
color-link diff-modified "yellow"
color-link diff-deleted "red"
color-link gutter-error "black,brightred"
color-link gutter-warning "brightred,default"
color-link cursor-line "black"

View File

@@ -15,6 +15,9 @@ color-link statusline "233,229"
color-link tabbar "233,229"
color-link indent-char "229"
color-link line-number "244"
color-link diff-added "34"
color-link diff-modified "214"
color-link diff-deleted "160"
color-link gutter-error "88"
color-link gutter-warning "88"
color-link cursor-line "229"

View File

@@ -10,6 +10,9 @@ color-link current-line-number "#868686,#1B1B1B"
color-link cursor-line "#1B1B1B"
color-link divider "#1E1E1E"
color-link error "#D2A8A1"
color-link diff-added "#00AF00"
color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-error "#9B859D"
color-link gutter-warning "#9B859D"
color-link identifier "#9B703F"

View File

@@ -16,6 +16,9 @@ color-link statusline "186,236"
color-link tabbar "186,236"
color-link indent-char "238,237"
color-link line-number "248,238"
color-link diff-added "34"
color-link diff-modified "214"
color-link diff-deleted "160"
color-link gutter-error "237,174"
color-link gutter-warning "174,237"
color-link cursor-line "238"

View File

@@ -20,6 +20,8 @@ set colorscheme twilight
Micro comes with a number of colorschemes by default. The colorschemes that you
can display will depend on what kind of color support your terminal has.
Omit color-link default "[fg color],[bg color]" will make the background color match the terminal's, and transparency if set.
Modern terminals tend to have a palette of 16 user-configurable colors (these
colors can often be configured in the terminal preferences), and additional
color support comes in three flavors.
@@ -93,6 +95,7 @@ variable `MICRO_TRUECOLOR` must be set to 1.
look its best. Use cmc-16 if your terminal doesn't support true color.
* `gruvbox-tc`: The true color version of the gruvbox colorscheme
* `github-tc`: The true color version of the Github colorscheme
* `material-tc`: Colorscheme based off of Google's Material Design palette
## Creating a Colorscheme
@@ -129,7 +132,7 @@ If you would like no foreground you can just use a comma with nothing in front:
color-link comment ",blue"
```
You can also put bold, or underline in front of the color:
You can also put bold, italic, or underline in front of the color:
```
color-link comment "bold red"

View File

@@ -62,6 +62,11 @@ quotes here but these are not necessary when entering the command in micro.
* `tab 'filename'`: opens the given file in a new tab.
* `tabmove '[-+]?n'`: Moves the active tab to another slot. `n` is an integer.
If `n` is prefixed with `-` or `+`, then it represents a relative position
(e.g. `tabmove +2` moves the tab to the right by `2`). If `n` has no prefix,
it represents an absolute position (e.g. `tabmove 2` moves the tab to slot `2`).
* `tabswitch 'tab'`: This command will switch to the specified tab. The `tab`
can either be a tab number, or a name of a tab.

View File

@@ -89,8 +89,9 @@ selection, micro's mouse support must first be disabled by turning the
`mouse` option off. The terminal, unlike micro, has no sense of different
buffers/splits and what the different characters being displayed are. This
means that for copying multiple lines using the terminal selection, you
should first disable line numbers (turn off the `ruler` option), otherwise
they might be part of your selection and copied.
should first disable line numbers and diff indicators (turn off the `ruler`
and `diffgutter` options), otherwise they might be part of your selection
and copied.
## Recommendations
@@ -103,7 +104,7 @@ The recommended method of copying is the following:
* If you are working over SSH, use the terminal keybinding
(Ctrl-Shift-c or Command-c) to perform copies. You must first disable
the `mouse` option to perform a terminal selection, and you may wish
to disable line numbers (`ruler` option) and close other splits. This
method will only be able to copy characters that are displayed on the
screen (you will not be able to copy more than one page's worth of
characters).
to disable line numbers and diff indicators (`ruler` and `diffgutter`
options) and close other splits. This method will only be able to copy
characters that are displayed on the screen (you will not be able to
copy more than one page's worth of characters).

View File

@@ -23,7 +23,7 @@ can change it!
| Shift+arrows | Move and select text |
| Alt(Ctrl on Mac)+LeftArrow | Move to the beginning of the current line |
| Alt(Ctrl on Mac)+RightArrow | Move to the end of the current line |
| Home | Move to the beginning of the current line |
| Home | Move to the beginning of text on the current line |
| End | Move to the end of the current line |
| Ctrl(Alt on Mac)+LeftArrow | Move cursor one word left |
| Ctrl(Alt on Mac)+RightArrow | Move cursor one word right |

View File

@@ -162,6 +162,8 @@ SelectUp
SelectDown
SelectLeft
SelectRight
SelectToStartOfText
SelectToStartOfTextToggle
WordRight
WordLeft
SelectWordRight
@@ -183,17 +185,21 @@ Save
SaveAll
SaveAs
Find
FindLiteral
FindNext
FindPrevious
Undo
Redo
Copy
CopyLine
Cut
CutLine
DuplicateLine
DeleteLine
IndentSelection
OutdentSelection
OutdentLine
IndentLine
Paste
SelectAll
OpenFile
@@ -207,9 +213,12 @@ HalfPageUp
HalfPageDown
StartOfLine
EndOfLine
StartOfText
StartOfTextToggle
ParagraphPrevious
ParagraphNext
ToggleHelp
ToggleDiffGutter
ToggleRuler
JumpLine
ClearStatus
@@ -242,6 +251,9 @@ JumpToMatchingBrace
Autocomplete
```
The `StartOfTextToggle` and `SelectToStartOfTextToggle` actions toggle between
jumping to the start of the text (first) and start of the line.
You can also bind some mouse actions (these must be bound to mouse buttons)
```
@@ -407,21 +419,21 @@ conventions for text editing defaults.
"ShiftDown": "SelectDown",
"ShiftLeft": "SelectLeft",
"ShiftRight": "SelectRight",
"AltLeft": "WordLeft",
"AltRight": "WordRight",
"AltLeft": "WordLeft", (Mac)
"AltRight": "WordRight", (Mac)
"AltUp": "MoveLinesUp",
"AltDown": "MoveLinesDown",
"CtrlShiftRight": "SelectWordRight",
"CtrlShiftLeft": "SelectWordLeft",
"AltLeft": "StartOfLine",
"AltLeft": "StartOfTextToggle",
"AltRight": "EndOfLine",
"AltShiftRight": "SelectWordRight", (Mac)
"AltShiftLeft": "SelectWordLeft", (Mac)
"CtrlLeft": "StartOfText", (Mac)
"CtrlRight": "EndOfLine", (Mac)
"AltShiftLeft": "SelectToStartOfLine",
"CtrlShiftLeft": "SelectToStartOfText", (Mac)
"ShiftHome": "SelectToStartOfLine",
"AltShiftLeft": "SelectToStartOfTextToggle",
"CtrlShiftLeft": "SelectToStartOfTextToggle", (Mac)
"ShiftHome": "SelectToStartOfTextToggle",
"AltShiftRight": "SelectToEndOfLine",
"CtrlShiftRight": "SelectToEndOfLine", (Mac)
"ShiftEnd": "SelectToEndOfLine",
@@ -445,7 +457,7 @@ conventions for text editing defaults.
"CtrlP": "FindPrevious",
"CtrlZ": "Undo",
"CtrlY": "Redo",
"CtrlC": "Copy",
"CtrlC": "CopyLine|Copy",
"CtrlX": "Cut",
"CtrlK": "CutLine",
"CtrlD": "DuplicateLine",
@@ -454,7 +466,7 @@ conventions for text editing defaults.
"CtrlT": "AddTab",
"Alt,": "PreviousTab",
"Alt.": "NextTab",
"Home": "StartOfLine",
"Home": "StartOfText",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",

View File

@@ -16,6 +16,22 @@ Here are the available options:
default value: `true`
* `autosave`: automatically save the buffer every n seconds, where n is the
value of the autosave option. Also when quitting on a modified buffer, micro
will automatically save and quit. Be warned, this option saves the buffer
without prompting the user, so data may be overwritten. If this option is
set to `0`, no autosaving is performed.
default value: `0`
* `autosu`: When a file is saved that the user doesn't have permission to
modify, micro will ask if the user would like to use super user
privileges to save the file. If this option is enabled, micro will
automatically attempt to use super user privileges to save without
asking the user.
default value: `false`
* `backup`: micro will automatically keep backups of all open buffers. Backups
are stored in `~/.config/micro/backups` and are removed when the buffer is
closed cleanly. In the case of a system crash or a micro crash, the contents
@@ -28,8 +44,8 @@ Here are the available options:
default value: `true`
* `basename`: in the infobar, show only the basename of the file being edited
rather than the full path.
* `basename`: in the infobar and tabbar, show only the basename of the file
being edited rather than the full path.
default value: `false`
@@ -60,14 +76,19 @@ Here are the available options:
default value: `true`
* `diffgutter`: display diff indicators before lines.
default value: `false`
* `encoding`: the encoding to open and save files with. Supported encodings
are listed at https://www.w3.org/TR/encoding/.
default value: `utf-8`
* `eofnewline`: micro will automatically add a newline to the file.
* `eofnewline`: micro will automatically add a newline to the end of the
file if one does not exist.
default value: `false`
default value: `true`
* `fastdirty`: this determines what kind of algorithm micro uses to determine
if a buffer is modified or not. When `fastdirty` is on, micro just uses a
@@ -75,10 +96,10 @@ Here are the available options:
This is fast, but can be inaccurate. If `fastdirty` is off, then micro will
hash the current buffer against a hash of the original file (created when
the buffer was loaded). This is more accurate but obviously more resource
intensive. This option is only for people who really care about having
accurate modified status.
intensive. This option will be automatically disabled if the file size
exceeds 50KB.
default value: `true`
default value: `false`
* `fileformat`: this determines what kind of line endings micro will use for
the file. UNIX line endings are just `\n` (linefeed) whereas dos line
@@ -151,6 +172,19 @@ Here are the available options:
default value: `false`
* `pluginchannels`: list of URLs pointing to plugin channels for downloading and
installing plugins. A plugin channel consists of a json file with links to
plugin repos, which store information about plugin versions and download URLs.
By default, this option points to the official plugin channel hosted on GitHub
at https://github.com/micro-editor/plugin-channel.
default value: `https://raw.githubusercontent.com/micro-editor/plugin-channel
/master/channel.json`
* `pluginrepos`: a list of links to plugin repositories.
default value: ``
* `readonly`: when enabled, disallows edits to the buffer. It is recommended
to only ever set this option locally using `setlocal`.
@@ -263,11 +297,33 @@ Here are the available options:
default value: `true`
* `xterm`: micro will assume that the terminal it is running in conforms to
`xterm-256color` regardless of what the `$TERM` variable actually contains.
Enabling this option may cause unwanted effects if your terminal in fact
does not conform to the `xterm-256color` standard.
Default value: `false`
---
Plugin options: all plugins come with a special option to enable or disable
them. The option is a boolean with the same name as the plugin itself.
By default, the following plugins are provided, each with an option to enable
or disable them:
* `autoclose`: automatically closes brackets, quotes, etc...
* `comment`: provides automatic commenting for a number of languages
* `ftoptions`: alters some default options depending on the filetype
* `linter`: provides extensible linting for many languages
* `literate`: provides advanced syntax highlighting for the Literate
programming tool.
* `status`: provides some extensions to the status line (integration with
Git and more).
* `diff`: integrates the `diffgutter` option with Git. If you are in a Git
directory, the diff gutter will show changes with respect to the most
recent Git commit rather than the diff since opening the file.
Any option you set in the editor will be saved to the file
~/.config/micro/settings.json so, in effect, your configuration file will be
created for you. If you'd like to take your configuration with you to another

View File

@@ -11,7 +11,7 @@ from the go plugin, which has the following file structure:
```
~/.config/micro/plug/go-plugin/
go.lua
info.json
repo.json
help/
go-plugin.md
```
@@ -35,34 +35,6 @@ is unlikely for a plugin to need to add plugin files at runtime or
syntax header files. No directory structure is enforced but keeping
runtime files in their own directories is good practice.
# Info file
The `info.json` for the Go plugin is the following:
```
{
"name": "go",
"description": "Go formatting and tool support",
"website": "https://github.com/micro-editor/go-plugin",
"install": "https://github.com/micro-editor/go-plugin",
"version": "1.0.0",
"require": [
"micro >= 2.0.0"
]
}
```
All fields are simply interpreted as strings, so the version does not
need to be a semantic version, and the dependencies are also only
meant to be parsed by humans. The name should be an identifier, and
the website should point to a valid website. The install field should
provide info about installing the plugin, or point to a website that
provides information.
Note that the name of the plugin is defined by the name field in
the `info.json` and not by the installation path. Some functions micro
exposes to plugins require passing the name of the plugin.
## Lua callbacks
Plugins use Lua but also have access to many functions both from micro
@@ -102,16 +74,6 @@ within. This is almost always the current bufpane.
All available actions are listed in the keybindings section of the help.
For callbacks to mouse actions, you are also given the event info:
```lua
function onMousePress(view, event)
local x, y = event:Position()
return false
end
```
These functions should also return a boolean specifying whether the bufpane
should be relocated to the cursor or not after the action is complete.
@@ -141,7 +103,12 @@ The packages and functions are listed below (in Go type signatures):
`-debug` flag, or binary built with `build-dbg`).
- `SetStatusInfoFn(fn string)`: register the given lua function as
accessible from the statusline formatting options
accessible from the statusline formatting options.
- `CurPane() *BufPane`: returns the current BufPane, or nil if the
current pane is not a BufPane.
- `CurTab() *Tab`: returns the current tab.
* `micro/config`
- `MakeCommand(name string, action func(bp *BufPane, args[]string),
completer buffer.Completer)`:
@@ -424,6 +391,9 @@ There are 6 default plugins that come pre-installed with micro. These are
programming tool.
* `status`: provides some extensions to the status line (integration with
Git and more).
* `diff`: integrates the `diffgutter` option with Git. If you are in a Git
directory, the diff gutter will show changes with respect to the most
recent Git commit rather than the diff since opening the file.
See `> help linter`, `> help comment`, and `> help status` for additional
documentation specific to those plugins.

Some files were not shown because too many files have changed in this diff Show More