Source code editor in pure Go.
Source code editor in pure Go.
gotodefinition
and autocomplete
below..go
files identifiers will jump to the identifier definition (needs gopls
).GoDebug
cmd).-lsproto
cmd line optionclangd
and gopls
tab
key (uses LSP).Get with git
the latest from the default master branch:
git clone https://github.com/jmigpin/editor
Build and run:
cd ./editor
go build
./editor
Windows platform compilation alternatives:
go build # shows one console window (will be hidden, but makes a flash)
go build -ldflags -H=windowsgui # hides the console window, but cmds will popup consoles
go build -tags=xproto # (not native, needs an x11 server to run)
go get -u github.com/jmigpin/editor
Please take care to distinguish between versions and ensure “go get” is actually getting the version you want.
This project is not using “vX” named directories. So version 3 is not in the directory “v3”, nor is it named “*/v3” in imports. Versions are being updated using the same import path. The downside of this is that go websites (ex: pkg.go.dev) don’t recognize versions tagged as 3 and above of this project.
Currently a release will be tagged with two tags that refer to the same version (ex: 3.3.0 and 1.3.3 is the same version).
Feel free to file an issue if you know of a better solution that doesn’t require to use an import path of a directory that might not exist (case of using “vX” import paths).
Usage of editor:
-carriagereturnrune int
replacement rune for carriage return (default 9834)
-colortheme string
available: light, acme, lightInverted, acmeInverted (default "light")
-commentscolor int
Colorize comments. Can be set to 0x1 to not colorize. Ex: 0xff0000=red.
-cpuprofile string
profile cpu filename
-dpi float
monitor dots per inch (default 72)
-font string
font: regular, medium, mono, or a filename (default "regular")
-fonthinting string
font hinting: none, vertical, full (default "full")
-fontsize float
(default 12)
-lsproto value
Language-server-protocol register options. Can be specified multiple times.
Format: language,fileExtensions,network{tcp|tcpclient|stdio},command,optional{stderr,nogotoimpl}
Format notes:
if network is tcp, the command runs in a template with vars: {{.Addr}}.
if network is tcpclient, the command should be an ipaddress.
Examples:
go,.go,stdio,"gopls serve"
go,.go,tcp,"gopls serve -listen={{.Addr}}"
cpp,".c .h .cpp .hpp .cc",stdio,"clangd"
python,.py,stdio,pylsp
python,.py,tcpclient,127.0.0.1:9000
python,.py,stdio,pylsp,"stderr nogotoimpl"
-plugins string
comma separated string of plugin filenames
-presavehook value
Run program before saving a file. Uses stdin/stdout. Can be specified multiple times. By default, a "goimports" entry is auto added if no entry is defined for the "go" language.
Format: language,fileExtensions,cmd
Examples:
go,.go,goimports
cpp,".cpp .hpp","\"clang-format --style={'opt1':1,'opt2':2}\""
python,.py,python_formatter
-scrollbarleft
set scrollbars on the left side (default true)
-scrollbarwidth int
Textarea scrollbar width in pixels. A value of 0 takes 3/4 of the font size.
-sessionname string
open existing session
-shadows
shadow effects on some elements (default true)
-sn string
open existing session
-stringscolor int
Colorize strings. Can be set to 0x1 to not colorize. Ex: 0xff0000=red.
-tabwidth int
(default 8)
-usemultikey
use multi-key to compose characters (Ex: [multi-key, ~, a] = ã)
-version
output version and exit
-wraplinerune int
code for wrap line rune, can be set to zero (default 8592)
-zipsessionsfile
Save sessions in a zip. Useful for 100+ sessions. Does not delete the plain file. Beware that the file might not be easily editable as in a plain file.
The editor has no configuration file. Use it within a script with your preferences (example editor.sh
):
#!/bin/sh
exec ~/path/editor \
--dpi=143 \
--fontsize=9 \
--colortheme=acme \
--commentscolor=0x008b00 \
--stringscolor=0x8b3100 \
--lsproto=go,.go,stdio,"gopls serve" \
--lsproto="cpp,\".c .h .cpp .hpp .cc\",stdio,clangd" \
--presavehook=go,.go,goimports \
--presavehook="cpp,\".c .h .cpp .hpp\",\"clang-format --someoption\"" \
"$@"
The editor has a top toolbar and columns. Columns have rows. Rows have a toolbar and a textarea.
These row toolbars are also textareas where clicking (buttonRight
) on the text will run that text as a command.
The row toolbar has a square showing the state of the row.
Commands in toolbars are separated by “|” (not to be confused with the shell pipe). If a shell pipe is needed it should be escaped with a backslash.
All internal commands start with an Uppercase letter. Otherwise it tries to run an existent external program.
Examples:
~/tmp/subdir/file1.txt | ls
ls
will run ls
at ~/tmp/subdir
~/tmp/subdir/file1.txt | ls -l \| grep fi
ls -l | grep fi
~/tmp/subdir/file1.txt
file1.txt
opens a new row to edit the same file.~/tmp
opens a new row located at that directory.gorename -offset $edFileOffset -to abc
guru -scope fmt callers $edFileOffset
grep -niIR someword
xdg-open $edDir
xterm
$font=mono
Toolbar commands
ListSessions
: lists saved sessionsSaveSession <name>
: save session to ~/.editor_sessions.jsonDeleteSession <name>
: deletes the session from the sessions fileNewColumn
: opens new columnNewRow
: opens new empty row located at the active-row directory, or if there is none, the current directory. Useful to run commands in a directory.ReopenRow
: reopen a previously closed rowSaveAllFiles
: saves all filesReloadAll
: reloads all filepathsReloadAllFiles
: reloads all filepaths that are filesColorTheme
: cycles through available color themes.FontTheme
: cycles through available font themes.Exit
: exits the programVersion
: shows editor version in the messages rowRow toolbar commands
These commands run on a row toolbar, or on the top toolbar with the active-row.
NewFile <name>
: create (and open) new file at the row directory. Fails it the file already exists.Save
: save fileReload
: reload contentCloseRow
: close rowCloseColumn
: closes row columnFind
: find string (ignores case)GotoLine <num>
: goes to line numberReplace <old> <new>
: replaces old string with new, respects selectionsStop
: stops current process (external cmd) running in the rowListDir [-sub] [-hidden]
: lists directory-sub
: lists directory and sub directories-hidden
: lists directory including hiddenMaximizeRow
: maximize row. Will push other rows up/down.CopyFilePosition
: output the cursor file position in the format “fileRuneCodes
: output rune codes of the current row text selection.FontRunes
: output the current font runes.OpenExternal
: open the row with the preferred external application (ex: useful to open an image, pdf, etc).OpenFilemanager
: open the row directory with the external filemanager.OpenTerminal
: open the row directory with the external terminal.LsprotoCloseAll
: closes all running lsp client/server connections. Next call will auto start again. Useful to stop a misbehaving server that is not responding.LsprotoRename <new-name>
: Renames the identifiers under the text cursor using the loaded lsp instance. Uses the row/active-row filename, and the cursor index as the “offset” argument.LsprotoCallers
: lists callers of the identifier under the text cursor using the loaded lsp instance. Uses the row/active-row filename, and the cursor index as the “offset” argument. Also known as: call hierarchy incoming calls.LsprotoCallees
: lists callees of the identifier under the text cursor using the loaded lsp instance. Uses the row/active-row filename, and the cursor index as the “offset” argument. Also known as: call hierarchy outgoing calls.LsprotoReferences
: lists references of the identifier under the text cursor using the loaded lsp instance. Uses the row/active-row filename, and the cursor index as the “offset” argument.GoRename [-all] <new-name>
: Renames the identifier under the text cursor. Uses the row/active-row filename, and the cursor index as the “offset” argument. Reloads the calling row at the end if there are no errors.gopls
(limited scope in renaming, but faster).-all
: calls gorename
to rename across packages (slower).GoDebug <command> [arguments]
: debugger utility for go programs (more at commands:godebug)GoDebugFind <string>
: find string in current selected annotation. Useful to rewind the annotations to the desired point.GoDebugTrace
: print all current callers that have not returned. Useful to aid in finding deadlocks.Row name at the toolbar (usually the filename)
Textarea commands
OpenSession <name>
: opens previously saved session<url>
: opens url in preferred application.<filename(:number?)(:number?)>
: opens filename, possibly at line/column (usual output from compilers). Check common locations like $GOROOT
and C include directories.<identifier-in-a-.go-file>
: opens definition of the identifier. Ex: clicking in Println
on fmt.Println
will open the file at the line that contains the Println
function definition.Output of GoDebug -help
:
Usage:
GoDebug <command> [arguments]
The commands are:
run build and run program with godebug data
test test packages compiled with godebug data
build build binary with godebug data (allows remote debug)
connect connect to a binary built with godebug data (allows remote debug)
Env variables:
GODEBUG_BUILD_FLAGS comma separated flags for build
Examples:
GoDebug run
GoDebug run -help
GoDebug run main.go -arg1 -arg2
GoDebug run -paths=dir1,file2.go,dir3 main.go -arg1 -arg2
GoDebug run -tags=xproto main.go
GoDebug run -env=GODEBUG_BUILD_FLAGS=-cover main.go
GoDebug run -network=ws -startexec=false -env=GOOS=js:GOARCH=wasm -o=static/main.wasm client/main.go
GoDebug test
GoDebug test -help
GoDebug test -run=mytest -v
GoDebug test a_test.go -test.run=mytest -test.v -test.count 5
GoDebug build -help
GoDebug build -addr=:8078 main.go
GoDebug build -network=ws -addr=:8078 -env=GOOS=js:GOARCH=wasm -o=static/main.wasm client/main.go
GoDebug connect -help
GoDebug connect -addr=:8078
GoDebug connect -network=ws -addr=:8078
GoDebug connect -network=auto --continueserving
Output of GoDebug run -help
:
Usage of GoDebug run:
-addr string
address to transmit debug data
-editorisserver
run editor side as server instead of client (default true)
-env string
string with env variables (ex: "a=1:b=2:...")
-network string
protocol to use to transmit debug data: [tcp, ws, unix, auto]. Option 'auto' detects a tcp client request to auto upgrade to http/websocket. Ex: useful to alternate between a debug session for a server (tcp) and a brower (websocket), without restarting the godebug cmd (use with -continueserving).
-nodebugmsg
omit debug messages
-o string
output filename
-paths string
comma-separated string of dirs/files to annotate (also see the "//godebug:annotate*" source code directives to set files to be annotated)
-sbr
Stringify bytes/runes as string (ex: [97 98 99] outputs as "abc") (default true)
-srclines
add src reference lines to the compilation such that in case of panics, the stack refers to the original src file (default true)
-startexec
start executable; useful to set to false in the case of compiling for js/wasm where the browser is the one starting the compiled file (default true)
-syncsend
Don't send msgs in chunks (slow). Useful to get msgs before a crash.
-toolexec string
a program to invoke before the program argument. Useful to run a tool with the output file (ex: wine)
-usepkglinks
Use symbolic links to some pkgs directories to build the godebug binary. Helps solving new behaviour introduced by go1.19.x. that fails when an overlaid file depends on a new external module. (default true)
-verbose
print extra godebug build info
-work
print workdir and don't cleanup on exit
Annotate files
By default, the main file will be annotated. Other files/directories can be added with the -dirs
and -files
command line options, or by inserting one of the following comments in the code (notice the lack of space after “//“):
//godebug:annotateoff # disable annotating
//godebug:annotateblock
//godebug:annotatefile
//godebug:annotatepackage
//godebug:annotatemodule
//godebug:annotateimport # use before an "import" line
# or specify a target
//godebug
<file> # absolute or relative to the current
//godebug
<pkg-path>
//godebug
<pkg-path> # any pkg path inside will annotate all
# example:
//godebug
golang.org/x/tools/godoc/util
The annotator will detect these comments and annotate accordingly.
Packages located in $GOROOT are not annotated.
Higher level //godebug:*
comments will override lower ones.
Example on how to bypass loops that would become too slow with debug messages being sent:
//godebug:annotateoff // don't annotate arg "v1"
func fn(v1 int){
//godebug:annotateblock // re-enable annotations
a:=0 // annotated
if a==0{
a++ // annotated
//godebug:annotateoff
a+=2 // *not* annotated
a+=3 // *not* annotated
for i:=0; i<10000;i++{
// *not* annotated
}
}
println(a) // annotated, not part of the disabled block
}
Also, to improve on the String()
limitation:
type myint int
func (v myint) String() string {
return f1(v)
}
// would cause endless loop with godebug calling t.String() at arg `v`
// but not if it the annotations are off
//godebug:annotateoff
func fn(v myint) string {
return fmt.Sprintf("%d", v)
}
String()
and Error()
methods are not annotated to avoid endless loops (the annotation would recursively call the method again).esc
key to stop the debug session. Check related shortcuts at the key/buttons shortcuts section.GoDebug -h
).The example below builds a binary for windows in another platform, for later remote debug:
GoDebug run -env=GO111MODULE=off main.go
GoDebug build -env=GOOS=windows -addr=:8008 main.go
... (copy the binary to the target platform and run it) ...
GoDebug connect -addr=:8008
~<digit>=path
: Replaces long row filenames with the variable. Ex.: a file named /a/b/c/d/e.txt
with ~0=/a/b/c
defined in the top toolbar will be shortened to ~0/d/e.txt
.$font=<name>[,<size>]
: sets the row textarea font when set on the row toolbar. Useful when using a proportional font in the editor but a monospaced font is desired for a particular program output running in a row. Ex.: $font=mono
.$scrollMode={auto}
: if the current bottom of the content is visible, auto scroll down when new content is added (ex: a cmd output).$termFilter
: same as $terminal=f
$terminal={f,k}
: enable terminal features.f
: Filter (remove) escape sequences from the output. Currently only the clear display sequence is interpreted in this mode which clears the output (usefull for running programs that want to discard old ouput).k
: redirect keyboard input to the running program to enable reading from standard input. Note: typing keys will not be seen in the textarea unless the running program outputs them (exceptions: “\n”) .$edName
: row name. $edDir
: row directory. $edFileOffset
: filename with offset position from active row cursor. Ex: “filename:#123”.$edFileLine
: index line from active row cursor (1-based). Ex: “12”.$edFileWord
: word at index from active row cursor. Ex: “myvar”. Usage ex: use go doc $edFileWord
with cursor at a receiver variable.$<name>=<string>
: set custom variable in a row toolbar to be set in the environment when running an external command.Clicking on
$myprog="a.out -arg1"
$myprog -arg2 | echo a${myprog}b
$myprog -arg2
will have the shell run a.out -arg1 -arg2
.echo a${myprog}b
will result in echoing aa.out -arg1b
.blue
: row file has been edited.orange
: row file doesn’t exist.black
: row currently active. There is only one active row.red
: row file was edited outside (changed on disk) and doesn’t match last known save. Use Reload
cmd to update.blue
: there are other rows with the same filename (2 or more).yellow
: there are other rows with the same filename (2 or more). Color will change when the pointer is over one of the rows.Plugins allow extra functionality to be added to the editor without changing the binary.
A plugin can be compiled and run with (will output a *.so
):
go build -buildmode=plugin plugin1.go
go build -buildmode=plugin plugin2.go
editor --plugins plugin1.so,plugin2.so
Functions that can be implemented by a plugin are (subject to changes - work-in-progress ):
func OnLoad(ed *core.Editor)
func AutoComplete(ctx context.Context, ed *core.Editor, cfb *ui.ContextFloatBox) (err error, handled bool) // error` is only considered if `handled` is true
func ToolbarCmd(ed *core.Editor, erow *core.ERow, part *toolbarparser.Part) bool
Note that plugins might need to be recompiled everytime there are changes in the libraries provided by the editor.
Editor events currently implemented (subject to changes - work-in-progress ):
PostNewERowEEventId // on erow creation
PostFileSaveEEventId // after a file is saved
PreRowCloseEEventId // before a row is closed
RowStateChangeEEventId // on row state change (duplicate rows also emit).
Plugins located at: ./plugins
.
gotodefinition_godef.go
: plugin that shows how to override the textarea click action and use godef instead of the default.autocomplete_gocode.go
: plugin that shows a context with suggestions for .go
files (uses gocode).rownames.go
: example plugin that shows how to access row names.eevents.go
: example plugin on how to access editor events.Global key/button shortcuts
esc
:f1
: toggle context float boxAutoComplete
esc
: close context float boxColumn key/button shortcuts
buttonLeft
:Row key/button shortcuts
ctrl
+s
: save filectrl
+w
: close rowctrl
+f
: warp pointer to “Find” cmd in row toolbarctrl
+h
: warp pointer to “Replace” cmd in row toolbarctrl
+n
: warp pointer to “NewFile” cmd in row toolbarctrl
+r
: warp pointer to “Reload” cmd in row toolbarbuttonLeft
on square-button: close rowbuttonLeft
: drag to move/resize rowbuttonMiddle
: close rowbuttonWheelUp
: adjust row vertical position, pushing other rows upbuttonWheelDown
: adjust row vertical position, pushing other rows downTextarea key/button shortcuts
left
: move cursor leftright
: move cursor rightup
: move cursor updown
: move cursor downhome
: start of lineend
: end of linedelete
: delete current runebackspace
: delete previous runepageUp
: page uppageDown
: page downctrl
+left
: jump to previous word startctrl
+right
: jump to next word endbuttonLeft
: move cursor to pointshift
+buttonLeft
: move cursor to point adding to selectionbuttonRight
: move cursor to point + run textarea commandbuttonWheelUp
: scroll upbuttonWheelDown
: scroll downbuttonWheelUp
on scrollbar: page upbuttonWheelDown
on scrollbar: page downshift
+left
: move cursor left adding to selectionshift
+right
: move cursor right adding to selectionshift
+up
: move cursor up adding to selectionshift
+down
: move cursor down adding to selectionshift
+home
: start of string adding to selectionshift
+end
: end of string adding to selectionctrl
+a
: select allctrl
+c
: copy to clipboardctrl
+v
: paste from clipboardctrl
+x
: cutbuttonMiddle
: paste from primaryctrl
+z
: undoctrl
+shift
+z
: redotab
(if selection is on): insert tab at beginning of linesshift
+tab
: remove tab from beginning of linesctrl
+k
: remove linesctrl
+alt
+up
: move line(s) upctrl
+alt
+down
: move line(s) downctrl
+alt
+shift
+down
: duplicate linesctrl
+d
: comment linesctrl
+shift
+d
: uncomment linesctrl
+buttonLeft
: select debug stepctrl
+buttonRight
: over a debug step: print the value.ctrl
+buttonRight
+shift
: over a debug step: print all previous values up to the debug step.ctrl
+buttonWheelUp
:ctrl
+buttonWheelDown
:ctrl
+f4
: show first debug stepctrl
+f5
: show last debug stepctrl
+f9
: clear all debug steps (clears memory, continues debugging)esc
: stop the debug session.tab
: inline code completion for file extensions registered with LSP.tab
insertion, press modkey
+tab
(ex: ctrl
, alt
, …).esc
: stop inline completion.When a new row is created, it is placed either below the current row (measuring available space), or in a “good position”.
The “good position” measures the available space between all rows, and uses the position with the most space.
The measuring of space is done as follows:
1) if the cursor is visible, measure space after visible cursor to the end of the textarea and use it if not smaller than two lines in height, otherwise use 2)
2) about 2/3 of the textarea
https://deps.dev/advisory/osv/GO-2022-0493
tab
complete