diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..33318bc2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: KurokuLabs +open_collective: gosublime +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: + - https://margo.sh/funding/ diff --git a/.github/workflows/gs-ci.yml b/.github/workflows/gs-ci.yml new file mode 100644 index 00000000..3f913ef6 --- /dev/null +++ b/.github/workflows/gs-ci.yml @@ -0,0 +1,22 @@ +on: [push, pull_request] +name: margo-ci +jobs: + margo-ci: + strategy: + matrix: + go-version: [1.13.x, 1.14.x] + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + steps: + - name: Setup + uses: actions/setup-go@v1 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout + uses: actions/checkout@v2 + with: + path: gs + - name: CI + env: + GOPATH: ${{ github.workspace }}/gs + run: go install -v gosublime/cmd/margo diff --git a/.gitignore b/.gitignore index 05139765..4616469e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +_before.py +_after.py bin/** pkg/** !.keep diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2988433b..00000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go -go: - - 1.8 - - 1.x -install: true -script: - - env GOPATH=$PWD go install -v gosublime/cmd/margo diff --git a/Ariana.sublime-color-scheme b/Ariana.sublime-color-scheme new file mode 100644 index 00000000..a8f707d2 --- /dev/null +++ b/Ariana.sublime-color-scheme @@ -0,0 +1,313 @@ +{ + "name": "Ariana", + "author": "DisposaBoy, Sublime HQ Pty Ltd, Dmitri Voronianski", + "variables": + { + "black": "hsl(0, 0%, 0%)", + "blue": "hsl(210, 50%, 60%)", + "blue2": "hsl(215, 33%, 29%)", + "blue3": "hsl(215, 40%, 21%)", + "blue4": "hsl(210, 13%, 45%)", + "blue5": "hsl(180, 36%, 54%)", + "blue6": "hsl(221, 12%, 69%)", + "green": "hsl(114, 31%, 68%)", + "grey": "hsl(0, 0%, 20%)", + "orange": "hsl(32, 93%, 66%)", + "orange2": "hsl(32, 85%, 55%)", + "orange3": "hsl(40, 94%, 68%)", + "pink": "hsl(300, 30%, 68%)", + "red": "hsl(357, 79%, 65%)", + "red2": "hsl(13, 93%, 66%)", + "white": "hsl(0, 0%, 100%)", + "white2": "hsl(0, 0%, 97%)", + "white3": "hsl(219, 28%, 93%)" + }, + "globals": + { + "foreground": "var(white3)", + "background": "var(blue3)", + "caret": "var(orange)", + "line_highlight": "var(blue2)", + "selection": "var(blue2)", + "selection_border": "var(blue4)", + "inactive_selection": "var(blue2)", + "misspelling": "var(red)", + "shadow": "color(var(black) alpha(0.25))", + "active_guide": "var(blue5)", + "stack_guide": "color(var(blue5) alpha(0.5))", + "highlight": "var(blue5)", + "find_highlight_foreground": "var(grey)", + "find_highlight": "var(orange3)", + "brackets_options": "underline", + "brackets_foreground": "var(orange)", + "bracket_contents_options": "underline", + "bracket_contents_foreground": "var(blue5)", + "tags_options": "stippled_underline", + "tags_foreground": "var(pink)" + }, + "rules": + [ + { + "name": "Comment", + "scope": "comment, punctuation.definition.comment", + "foreground": "var(blue6)" + }, + { + "name": "String", + "scope": "string", + "foreground": "var(green)" + }, + { + "name": "Punctuation", + "scope": "punctuation.definition", + "foreground": "var(blue5)" + }, + { + "name": "Number", + "scope": "constant.numeric", + "foreground": "var(orange)" + }, + { + "name": "Built-in constant", + "scope": "constant.language", + "foreground": "var(red)", + "font_style": "italic" + }, + { + "name": "User-defined constant", + "scope": "constant.character, constant.other", + "foreground": "var(pink)" + }, + { + "name": "Member Variable", + "scope": "variable.member", + "foreground": "var(red)" + }, + { + "name": "Keyword", + "scope": "keyword - keyword.operator, keyword.operator.word", + "foreground": "var(pink)" + }, + { + "name": "Operators", + "scope": "keyword.operator", + "foreground": "var(red2)" + }, + { + "name": "Punctuation", + "scope": "punctuation.separator, punctuation.terminator", + "foreground": "var(blue6)" + }, + { + "name": "Punctuation", + "scope": "punctuation.section", + "foreground": "var(white)" + }, + { + "name": "Accessor", + "scope": "punctuation.accessor", + "foreground": "var(blue6)" + }, + { + "name": "Annotation Punctuation", + "scope": "punctuation.definition.annotation", + "foreground": "var(blue5)" + }, + { + "name": "JavaScript Dollar", + "scope": "variable.other.dollar.only.js, variable.other.object.dollar.only.js, variable.type.dollar.only.js, support.class.dollar.only.js", + "foreground": "var(blue5)" + }, + { + "name": "Storage", + "scope": "storage", + "foreground": "var(red)" + }, + { + "name": "Storage type", + "scope": "storage.type", + "foreground": "var(pink)", + "font_style": "italic" + }, + { + "name": "Entity name", + "scope": "entity.name.function", + "foreground": "var(blue5)" + }, + { + "name": "Entity name", + "scope": "entity.name - (entity.name.section | entity.name.tag | entity.name.label)", + "foreground": "var(orange)" + }, + { + "name": "Inherited class", + "scope": "entity.other.inherited-class", + "foreground": "var(blue5)", + "font_style": "italic underline" + }, + { + "name": "Function argument", + "scope": "variable.parameter", + "foreground": "var(orange)" + }, + { + "name": "Language variable", + "scope": "variable.language", + "foreground": "var(red)", + "font_style": "italic" + }, + { + "name": "Tag name", + "scope": "entity.name.tag", + "foreground": "var(red)" + }, + { + "name": "Tag attribute", + "scope": "entity.other.attribute-name", + "foreground": "var(pink)" + }, + { + "name": "Function call", + "scope": "variable.function, variable.annotation", + "foreground": "var(blue)" + }, + { + "name": "Library function", + "scope": "support.function, support.macro", + "foreground": "var(blue)", + "font_style": "italic" + }, + { + "name": "Library constant", + "scope": "support.constant", + "foreground": "var(pink)", + "font_style": "italic" + }, + { + "name": "Library class/type", + "scope": "support.type, support.class", + "foreground": "var(blue)", + "font_style": "italic" + }, + { + "name": "Invalid", + "scope": "invalid", + "foreground": "var(white2)", + "background": "var(red)" + }, + { + "name": "Invalid deprecated", + "scope": "invalid.deprecated", + "foreground": "var(white2)", + "background": "var(orange2)" + }, + { + "name": "YAML Key", + "scope": "entity.name.tag.yaml", + "foreground": "var(blue5)" + }, + { + "name": "YAML String", + "scope": "source.yaml string.unquoted", + "foreground": "var(white3)" + }, + { + "name": "markup headings", + "scope": "markup.heading", + "font_style": "bold" + }, + { + "name": "markup headings", + "scope": "markup.heading punctuation.definition.heading", + "foreground": "var(red2)" + }, + { + "name": "markup h1", + "scope": "markup.heading.1 punctuation.definition.heading", + "foreground": "var(red)" + }, + { + "name": "markup links", + "scope": "string.other.link, markup.underline.link", + "foreground": "var(blue)" + }, + { + "name": "markup bold", + "scope": "markup.bold", + "font_style": "bold" + }, + { + "name": "markup italic", + "scope": "markup.italic", + "font_style": "italic" + }, + { + "name": "markup bold/italic", + "scope": "markup.italic markup.bold | markup.bold markup.italic", + "font_style": "bold italic" + }, + { + "name": "markup hr", + "scope": "punctuation.definition.thematic-break", + "foreground": "var(orange)" + }, + { + "name": "markup numbered list bullet", + "scope": "markup.list.numbered.bullet", + "foreground": "var(green)" + }, + { + "name": "markup blockquote", + "scope": "markup.quote punctuation.definition.blockquote, markup.list punctuation.definition.list_item", + "foreground": "var(orange)" + }, + { + "name": "markup code", + "scope": "markup.raw", + "background": "color(var(blue2) alpha(0.38))" + }, + { + "name": "markup code", + "scope": "markup.raw.inline", + "background": "color(var(blue2) alpha(0.5))" + }, + { + "name": "markup punctuation", + "scope": "(text punctuation.definition.italic | text punctuation.definition.bold)", + "foreground": "var(pink)" + }, + { + "name": "diff.header", + "scope": "meta.diff, meta.diff.header", + "foreground": "var(pink)" + }, + { + "name": "diff.deleted", + "scope": "markup.deleted", + "foreground": "var(red)" + }, + { + "name": "diff.inserted", + "scope": "markup.inserted", + "foreground": "var(green)" + }, + { + "name": "diff.changed", + "scope": "markup.changed", + "foreground": "var(orange)" + }, + { + "name": "CSS Properties", + "scope": "support.type.property-name", + "foreground": "var(white3)" + }, + { + "scope": "constant.numeric.line-number.match", + "foreground": "var(red)" + }, + { + "scope": "message.error", + "foreground": "var(red)" + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 13691864..2d69810b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,1078 +1,555 @@ +## Links: +https://margo.sh/donate - Help support future development of GoSublime. -Help shape the future of margo and GoSublime. -See https://margo.sh/gosublime-future for more details. - -Thank you all for the great feedback so far!!! :D -Base on your feedback, we've decided to join https://margo.sh/s/patreon/ and https://margo.sh/s/twitter/. -We plan to explore more direct funding through Stripe soon. More updates will be posted to Twitter. +https://margo.sh/b/hello-margo - A short introduction to margo. +https://margo.sh/b/motd - Get notified when GoSublime has a new release. -**Donate:** - -If you find GoSublime useful and would like to support me and future development of GoSublime, -please donate via one of the available methods on https://github.com/DisposaBoy/GoSublime#donations +--- +## Changes -**margo:** +## 20.06.14 -The new version of margo is close to being ready for real usage. -If you'd like to test it out, press `ctrl+.`,`ctrl+x` or `cmd+.`,`cmd+x` -to open the extension file and then save it and restart sublime text. - -Please make sure to read all the comments, as enabling it will affect GoSublime features. - -**Changes:** - -## 18.05.19 - * Improve ligature support in the `GoSublime: Go` syntax +This release contains a number of features and bug fixes that have been worked on over the last few months. -## 18.04.30-1 - * fix margo build failure in some cases when the go compiler is up/down-graded +_You will need to restart Sublime Text for all changes to take effect_ -## 18.04.19-1 - * fix margo build failure when `use_gs_gopath` is enabled +- Add new GoCmd{} option `Humanize` to make `go test` and `go re/play` (in test mdoe) output more readable (using https://github.com/dustin/go-humanize). -## 18.03.26-1 - * update gocode - * fix gocode completion showing duplicate function parameter names - * add support for restricting the files in which events are triggered - * (hopefully) fix an issue where some users were experiencing laggy input - -## 18.03.23-1 - * GOPATH is automatically set to the internal GOPATH when editing margo source files - this allows for things like linters and gocode to work when editing `margo.go` - - * margo is now only automatically restarted if `go test` succeeds - this further supports linters by not restarting when `margo.go` compilation fails - - * add a default linter pattern for gometalinter - the following reducer/linter should now work without any additional setup - - ``` - &golang.Linter{Name: "gometalinter", Args: []string{"--fast"}}, - - ``` - - * add support for sending settings to margo - in the GoSublime settings, Sublime Text preferences or project settings - the entry `"margo": {...}` will be send to margo where reducers can make use of it - by accessing `mx.Editor.Settings` e.g. - - gosublime settings: - ``` - "margo": { - "message": "hello world from the GoSublime settings", - } - ``` - - project settings: - ``` - "margo": { - "message": "hello world from the project settings", - } - ``` + - large numbers are split up using commas + - 123456 ns/op is converted to µs/op, etc. + - 123456 B/op is converted to KiB/op, etc. - margo.go - ``` - mg.Reduce(func(mx *mg.Ctx) *mg.State { - var settings struct { - // due to limitations in the codec pkg we need to add struct tags - // unless we use the exact (capitalized name) in the editor settings - Status string `codec:"status"` - } - err := mx.Editor.Settings(&settings) - switch err { - case mg.ErrNoSettings: - // when the `Started` action is dispatched, no client data is present - // and we therefore have no settings - return mx.State - case nil: - return mx.AddStatus(settings.Status) - default: - return mx.AddStatusf("cannot decode settings: %v", err) - } - }), - ``` -## 18.03.21-1 - * fix exception due to missing import? + To enabled it, use: -## 18.03.20-1 - * fix a case where (old) margo compilation fails because the Go version cannot - be determined, but the sanity check shows the correct version - * move some initializations off the ui thread to avoid ui freezes on startup - * fix the `golang.GoInstallDiscardBinaries` linter failing due to invalid dirname + &golang.GoCmd{ + Humanize: true, + } -## 18.03.19-1 - * disable the `autoinst` setting by default - * fix a Python buffering issue that sometimes caused the ui to freeze - * speedup sh-bootstrap/Sublime Text startup + e.g. output: -## 18.03.16-1 - * support for context-aware snippets have now been added in the new version of margo. - just add `golang.Snippets,` to your reducers to enable it. + goos: linux + goarch: amd64 + pkg: margo.sh/vfs + BenchmarkPoke/Miss-8 388,868 2.952 µs/op + BenchmarkPoke/Hit-8 1,739,704 684 ns/op + PASS - see https://github.com/disposablue/margo/blob/master/extension-example/extension-example.go - -## 18.03.05-1 - * if no status is set, the status markers (dots) are no longer shown - * sh-bootstrap.go is now built with `go build` to improve startup time in go1.10 - * path handling on Windows was improved - * linter support should now be almost complete. - * add support for displaying issues via `ctrl+.`,`ctrl+e` or `cmd+.`,`cmd+e` - * errors for all files in the package are tracked - * the error count in the status bar is always visible if there are errors in other files - - see the example extension https://github.com/disposablue/margo/blob/master/extension-example/extension-example.go for examples of builtin linters. - - -## 18.02.17-2 - * stop linking sqlite3 to avoid cgo-related compilation errors - -## 18.02.17-1 - * fix a compilation error introduced in r18.02.16-1 - -## 18.02.16-1 - * The new version of margo is close to being ready for real usage. - If you'd like to test it out, press `ctrl+.`,`ctrl+x` or `cmd+.`,`cmd+x` - to open the extension file and then save it or restart sublime text - * Highlights: - * less dependence on Python, so development should be a lot easier going forward - * it comes with integrated support for GoImports - * gocode integration now supports more options like autobuild, showing function params and autocompleting packages that have not been imported - -## 18.01.17-1 - * update gocode - * sync the settings when the active view changes to avoid them going out-of-sync when switching projects - * add support for exporting env vars into ST. - see docs for the setting `export_env_vars` (`ctrl+., ctrl+4`, `super+., super+4` on mac) - * sync all project settings, not just `env` - -## 17.12.17-1 - * fix failure to list some packges in the imports palette - * update gocode - -## 17.12.08-1 - * fix broken commenting when the Go package is disabled - -## 17.11.27-1 - * use the old GS syntax definitions instead of the new ones from ST to avoid regressions - -## 17.11.25-1 - * use the latest Sublime Text Go syntax - * convert all our existing syntax definitions to .sublime-synta - * keep track of the sh.bootstrap output and include it in the Sanity Check - -## 17.11.14-1 - * Fix failure to list individual Test|Benchmark|Example functions in the test palette - -## 17.11.13-1 - * Change the prefix for identifiers in `*_test.go` files to tilde(~) - to prevent cluttering the declartion palette when searching for (un-)exported identifiers - - * Move sh.py/shell bootstrapping into Go and always run it through the shell. - - This should fix 2 bugs: - - 1. If you had multiple versions of Go installed, one chosen seemingly at random: - - * Go is installed via your package manager and is setup correctly - * You then installed a custom version of Go and set GOROOT (in your shell) - * It has no effect even though your custom binary appear first in $PATH - - This might also fix other cases where the sanity check shows correct settings but compilation fails. - - 2. multi-path GOPATH, PATH, etc. in Fish and similar shells are seemingly ignored - - In Fish and similar shells where PATH (and other colon separated vars) are lists; - when these vars are echoed, they are output as `"dir1" "dir2"` instead of `"dir1":"dir2"` - so when GS sees it, it thinks it's 1 non-existend dir `"dir1 dir2"`. - -## 17.10.15 - * update gocode - * fix failure to display `time` (and other packages) in the imports list + Known bugs: -## 17.08.23 + - The output fields are not aligned - * update gocode +- Add new reducer golang.GoGenerate -## 17.03.05 - * assign default GOPATH as is done in go1.8 - * don't follow symlinks when scanning for package lists + It adds a UserCmd (cord `ctrl/cmd+.`,`ctrl/cmd+.c`) named `Go Generate` that calls `go generate` in the closest go package (current dir or parent dirs). -## 17.02.16 - * update gocode + It can be enabled with: -## 16.07.09-1 - * update gocode + &golang.GoGenerate{ + Args: []string{"-v", "-x"}, + }, -## 16.06.02-1 - * update gocode - * if you're using Go 1.7 beta and experience issues with completion, you're advised to downgrade back to 1.6 +- Auto-completion now works when the line ends with a dot (.) -## 16.05.07-1 - * Add initial support for MarGo extensions. - Press ctrl+.,ctrl+./super+.,super+. and type "Edit MarGo Extension" to open the the extension file. - If you your $GOPATH/src contains directories with lots of files or you'd otherwise like - to skip when looking up import paths, you can do so by configuring the `ImportPaths` option: - - package gosublime +- Add new reducer golang.AsmFmt - import ( - "disposa.blue/margo" - "disposa.blue/margo/meth/importpaths" - "path/filepath" - "strings" - ) + It does code fmt'ing for `.s` files using https://github.com/klauspost/asmfmt - func init() { - margo.Configure(func(o *margo.Opts) { - o.ImportPaths = importpaths.MakeImportPathsFunc(func(path string) bool { - // note: the default filter includes node_modules + It formats `.s` files when they are saved, or the fmt cord `ctrl+.`,`ctrl.f` is pressed. - // use the default filter - return importpaths.PathFilter(path) && - // don't descened into huge node_modules directory - !strings.Contains(path, filepath.Base("node_modules")) - }) - }) - } +- Add new reducer &web.Prettier{} + It does code fmt'ing using https://github.com/prettier/prettier + By default It fmt's CSS, HTML, JS, JSON, JSX, SVG, TS, TSX and XML files. -## 16.05.03-2 - * fallback to the internal MarGo fmt if `fmt_cmd` fails + To specify the list of langs to fmt set the `Langs` field: -## 16.05.03-1 - * fix incomplete gocode update + &web.Prettier{ + // Langs: []mg.Lang{mg.JS}, // only fmt .js files + Langs: web.PrettierDefaultLangs, + }, -## 16.05.01-1 - * update gocode - * add struct fields to the declarations palettes + You might also need to `import "margo.sh/web"`. -## 16.04.29-1 - * the imports list (ctrl+.,ctrl+p/super+.,super+p) is now sourced from source code packages only - and recognises vendored packages + You will need to install prettier separately. -## 16.04.08-2 - * If you use the `fmt_cmd` setting with `goimports` or any other slow command - you should read and understand the `ipc_timeout` setting documented in `GoSublime.sublime-settings` +- Add new Lang constants: mg.HTML, mg.SVG and mg.XML -## 16.04.08-1 - * added a new SUPPORT.md file calrify what level of support can be expected from use of GoSublime - * you are advised to reach and understand its contents +- Add mgutil.SplitWriter a writer that writes to an underlying writer in split chunks e.g. lines somewhat similar to bufio.scanner -## 16.03.22-1 - * add new pseudo env var _dir (`dirname($_fn)`) and do env var substitution on fmt_cmd - * use `"fmt_cmd": ["goimports", "-srcdir", "$_dir"]` for newer version of goimports +- `go.play` and `go.replay` (cord `ctrl/cmd+.`,`ctrl/cmd+r`) now works in in unsaved `_test.go` files. -## 16.01.09-1 - * Output GOROOT and GOPATH to the ST console when they change +- `go.replay` now runs the Benchmark\* func surrounding the cursor. -## 15.12.31-1 - * Update gocode (struct field completion, go15vendor completion, etc.) + Compared to `ctrl/cmd+shift+left-click`, it also runs tests. -## 14.02.25-1 - * added setting `installsuffix`. this should help enabling completion and pkg importing - for appengine. set this to `appengine` and add the appengine goroot to your GOPATH e.g. - { - "installsuffix": "appengine", - "env": { - "GOPATH": "$YOUR_OWN_GOPATH:$PATH_TO_APPENGINE/goroot" - } - } + Known bugs: - * added setting `ipc_timeout`. if you're experiencing issues with code completion - and the error is `Blocking Call(gocode_complete): Timeout`, set this setting to `2`, or `3`, etc.. - this value is the number of seconds to wait for ipc response from margo before timing out. - **note: blocking ipc calls like code completion will freeze the ui if they take too long** + - It currently ignores the TestArgs and BenchmarkArgs options of the golang.TestCmds reducer. -## 13.12.26-1 - * when the key binding `ctrl+dot`,`ctrl+r` is pressed, 9o no longer gains focus +- mg.CmdCtx supports a new option `Verbose`, -## 13.12.21-2 - * setting `autocomplete_live_hint` was renamed to `calltips` and enabled by default. - this setting make functions signatures appear in the status bar when you place the - cursor in a function call - * the completion_options command was dropped from margo and therefore mg9.completion_options was removed - * the shell_pathsep setting was removed + When `cx.Verbose = true`the commands that are run are printed to the output prefixed with `#`. -## 13.12.21-1 - * make GoSublime's quick panels use a monospace font - * add a prefix to the declarations panels: - `+` indicates exported identifiers - `-` indicates non-exported identifiers - * in the declarations panels, init functions are now suffixed with ` (filename)` - * in the declarations panels, const declarations are now suffixed with ` (value)` e.g. `const StatusTeapot (418)` - * add syntax definitions for the template builtin functions + e.g. output: -## 13.12.19-1 - * the OS X key bindings have been removed + [ `replay` | done ] + # go test -test.run=. -test.bench=^BenchmarkPoke$ + goos: linux + [...] - * a copy has been provided below. you may change the "keys" as you wish and place it inside - your user key bindings (menu Preferences > Key bindings - User) to restore the functionality + It's enabled for `go.play` and `go.replay` (cord `ctrl/cmd+.`,`ctrl/cmd+r`). - { - "keys": ["shift+space"], - "command": "auto_complete", - "args": {"disable_auto_insert": true, "api_completions_only": true, "next_completion_if_showing": false}, - "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] - } +- Issues without a valid tag are now defaulted to `mg.Error` instead of being ignored. + This fixes some cases where the error palette shows errors, but the status and HUD doesn't. -## 13.12.17-1 - * give string decoding priority to utf-8 over the system's preferred encoding +- Fix some cases where issues are reported in the wrong file or incorrectly anchored to the current file. -## 13.12.15-1 - * remove the ctrl+s, etc. key bindings and fmt the file during the save event. +- goutil.IsPkgDir() and other functions now use the VFS, so should touch the disk less. -## 13.12.14-2 - * the autocompletion key bindings on OS X have been changed to shift+space +## 20.03.09 -## 13.12.14-1 - * added new setting `fmt_cmd` to allow replacing margo's fmt with an external gofmt compatible command like like https://github.com/bradfitz/goimports. see the default config for documentation - * as a last resort, GoSublime will now try to ignore (by replacement) any bytes that cannot be decoded as utf-8 in places that handle strings (like printing to the console) - * fix the missing `Run {Test,Example,Benchmark}s` entries in the .t palette +This release fixes a couple bugs: -## 13.10.05-1 - * sync gocode +- GO111MODULE=off is set after building, in cases where GO111MODULE wasn't already set by the user. +- An update message is shown even when the local GoSublime version is greater than that reported by the server. -## 13.09.07-1 - * remove error syntax highlighting of lone percentage signs in strings +## 20.03.01 -## 13.07.29-1 - * the .p method of finding packages was reverted. as a result `use_named_imports` has no effect +This release fixes a margo build failure when upgrading to go1.14. -## 13.07.28-1 - * the behaviour of `$GS_GOPATH` has change, please see `Usage & Tips` `ctrl+dot,ctrl+2` - section `Per-project settings & Project-based GOPATH` for details +## 20.02.01 - * MarGo will now attempt to automatically install packages when you import a package that doesn't exist - or when completion fails. see the default settings file, `ctrl+dot,ctrl+4` for more details - about the `autoinst` setting +This release focuses on fixing a performance issue due to a failure to resetting all cached data prematurely. - * a new setting was added to allow using `GS_GOPATH` exclusively. see the default settings file, - `ctrl+dot,ctrl+4` for more details on the `use_gs_gopath` setting +- Cache some files in memory to avoid re-reading from disk every time. - * a new setting to allow importing packages with their package name was added. - see the default settings file, `ctrl+dot,ctrl+4` for more details on the `use_named_imports` setting +- The `&nodejs.PackageScripts{}` reducer now uses `yarn` instead of `npm` if the `yarn.lock` file is present. +## 20.01.01 -## 13.07.23-1 - * update gocode +This release mainly focuses on under-the-hood improvements for module support. -## 13.07.22-1 - * update gocode +- The default auto-completion import mode has been changed to `Kim-Porter`, our solution for auto-completion and package/module going forward. -## 13.07.17-1 - * the behaviour of 9o output scrolling has changed. instead of attempting to show the end - of the output, the start of the output will be shown instead. - if you preferred the old behaviour, use the new setting `"9o_show_end": true` + One side-effect of this change is that unimported-packages support is less reliable but we feel this is a small drawback when compared to the much improved auto-completion support. -## 13.07.14-1 - * fix comment toggling when the `Go` package is disabled + We plan to remove support for switching import modes in the future, but if you would like to revert to the previous default (bearing in mind auto-completion might stop working), configure the `MarGocodeCtl` reducer as follows: -## 13.07.12-1 - * update gocode + ```go + &golang.MarGocodeCtl{ + ImporterMode: golang.SrcImporterWithFallback, + } + ``` -## 13.07.06-2 - * the symbols [ ] ( ) { } , . are now treated as puctuation (they might be syntax highlighted) +- The Go/TypeCheck linter is now more complete and should be able to type-check (without failure) all packages for which auto-completion is available. + This linter offers typechecking (like the gotype tool) but can work on unsaved files and while you type and is faster a full `go install` lint. -## 13.07.06-1 - * the various operator groups, in addition to semi-colons are now treated as `operators` so they should now be syntax highlighted + To enable add the following reducer to your `margo.go` file: -## 13.07.03-1 - * log MarGo build failure + ```go + &golang.TypeCheck{}, + ``` -## 13.07.01-1 - * add user aliases to the 9o completion - * fix broken arrows keys in 9o completion - * 9o completion no longer contains the history command prefix (^1 ^2 etc.) (the commands are still shown) +- Some HTTP handler snippets have been added and are offered in files that `import "net/http"`. -## 13.06.30-4 - * try not to init() GoSublime more than once +## 19.10.22 -## 13.06.30-3 - * the `up` and `down` arrows keys now traverses the 9o history when the cursor is in the prompt +- API BREAKAGE: + ParseWithMode and ParseWithMode now takes a `*mg.Ctx` instead of a `mg.KVStore`. -## 13.06.30-2 - * added support for aliases via the setting `9o_aliases`, see the default settings files for documentation +- Add experimental support for auto-completion and type-checking in go modules. -## 13.06.30-1 - This update brings with it a new `GoSublime: Go` syntax definition. - If you get an error complaining about GoSublime .tmLanguage file, - you should be able to fix it by closing all `.go` files and restarting Sublime Text. - If you're using the `GoSublime-next.tmLanguage` please delete the file `Packages/User/GoSublime-next.sublime-settings` (if it exists). - On update(and restart), all views using syntax files with the base-name `GoSublime.tmLanguage` - or `GoSublime-next.tmLanguage` will be automatically changed to `GoSublime: Go`. - Hopefully this change will go smoothly. +- Add experimental reducer `&golang.TypeCheck{}`. + It's a linter that does a full type-check as you type (even in unsaved files). + It can be thought of as a replacement for the `gotype` binary of old. - For all other bugs relating to the new syntax definition (e.g. completion stops working) - please add a comment to https://github.com/DisposaBoy/GoSublime/issues/245 + NOTE: This is purely an experiment used primarily for testing the package importer + and type-checking and will probably break randomly, if it works at all. - For all other feature requests or bugs, please open a new issue. + With that said, the plan is to clean it up and develop it further in the future. - additionally: +- The Ariana color scheme has been tweaked to improve readability. - * there is a new pre-defined variable _nm that is the base name of the current view - * all pre-defind env vars (_fn, _wd, etc.) are now defined globally and will appear within the - environment of all 9o command even when run through your shell +- Add a `‣` prefix to status items and reduce the space between them. +- Add langs `mg.GoMod` and `mg.GoSum` for `go.mod` and `go.sum` files, respectively. + For convenience, `goutil.Langs` now holds the list of all Go-related langs + and Go linters are now available in `go.mod` and `go.sum`. +- The tasks count styled has been changed to `Tasks ➊➋➌`. + The status animates between `Tasks ➊➋➌` and `Tasks ➀➁➂` while there are tasks less than 16s old. -## 13.06.29-2 - * show the `go build` output when (re-)installing MarGo - * show the `go version` output on startup - * fix the main menu and command palette pointing to the wrong error log file +- The issue count styled has been changed to `Error ➊ꞏ🄋`. -## 13.06.29-1 - * added 9o `echo` command - * added two new env vars: - `$_wd (or $PWD)` contains the 9o working directory - `$_fn` contains the abs path to the current active view/file (if available) - * env vars on the 9o command line are expanded before the command is run. see 9o `help` + NOTE: The meanings of the numbers have been reverted. -## 13.06.22-1 - * NOTE: if you have your own GoSublime snippets, the meaning of `global` has changed. - It will no longer be `true` unless a package is fully declared and the cursor - is below the line on which the package was declared + Previously, given `1/2 Errors`, there was 1 issue with tag `Error` in this view, and there was a total 2 errors in all views. + The new meanings `Error ➊ꞏ🄋` is: ➊ is the number issues in the current view and 🄋 is the number issues in other views. + Only first number is highlighted if there are issues in the current view. + Likewise, when there are issues, but none in the current view, only the second number is highlighted. -## 13.06.16-2 - * added support for automatically setting the `GoSublime: HTML` syntax to certain extensions. See the default settings file (ctrl+dot,ctrl+4) for documentation on the `gohtml_extensions` setting +- Don't show the `func` prefix in the calltip status. The parens already make it obviously a function. -## 13.06.16-1 - * all undefined 9o commands are now run through your shell. As always, commands can manually be run through the with the `sh` command e.g. `sh echo 123` command +## 19.06.16 -## 13.06.15-1 - * based on the feedback I recieved I integrated with the shell a little... - * I added support for shells: bash, cygwin/msys/git bash, fish, zsh, rc, etc. - * see https://github.com/DisposaBoy/GoSublime/blob/master/articles/shell.md for more details +- Fix a deadlock/freeze (seen on Mac OS) when starting up with multiple windows open. +- Fix an issue where the active window loses focus when starting up. -## 13.06.05-1 - * added the shell env var and shell setting to the sanity check output +## 18.11.28 +This release introduces the HUD and comes with many improvements to snippets and a tweaked version of the Mariana color schemed named Ariana. -## 13.06.03-1 - * I added a small article about running [golint](https://github.com/golang/lint) and other user-commands for linting - https://github.com/DisposaBoy/GoSublime/blob/master/articles/golint.md +- The HUD is an output panel that's automatically populated with info traditionally found in the status bar and various pop-ups/tool-tips. -## 13.06.02-1 - * changed GoSublime home dir to Packages/User/GoSublime/[PLATFORM] - * changed margo exe name to gosublime.margo_[VERSION]_[GO_VERSION].exe - * sorry for any breakages + Currently, the following info will be displayed there: -## 13.06.01-1 - * fix missing method snippet when using GoSublime-next.tmLanguage + - The `Issues` status, including the error messages for the current line. -## 13.05.27-3 - * make sure the output panel is always accessed from the main thread + - The `GocodeCalltips` status, including positional highlighting of params and return statement. -## 13.05.27-2 - * document the `fn_exclude_prefixes` setting + It's bound to the keys `ctrl+.`,`ctrl+0`. -## 13.05.27-1 - * added basic syntax highlighting for go templates (embedded within `{{` and `}}`) - * inside .go files, `raw` strings(only) now has highlighting for go templates - *note* this is only available on the GoSublime-next syntax which will be set to the default - for .go files soon, see https://github.com/DisposaBoy/GoSublime/issues/245 - * for html files, the extension .gohtml will yield html highlighting, snippets, etc. - as normal .html files do, with addition of go template highighting within `{{` and `}}` - see https://github.com/DisposaBoy/GoSublime/issues/252 + You can manually bind it to another key. e.g. via `Preferences > Key Bindings`: -## 13.05.26-2 - * 9o: `tskill` without args, now opens the `pending tasks` palette + ```json + { + "keys": ["ctrl+0"], + "command": "margo_show_hud", + }, + ``` -## 13.05.26-1 - * fix `mg -env` in st3 on windows +- Several new snippets have been added and old ones improved. -## 13.05.12-7 - * add basic support for injecting commands into 9o. - contact me if you intend to make use of this feature + - AppendSnippet: `name = |` suggests: -## 13.05.12-6 - * 9o `hist` now honours `9o_instance` + - `name = append(name, ...)` + - `slice = append(slice[:len(slice):len(slice)], ...)` -## 13.05.12-5 - * more 9o `cd`, always chdir + - DeferSnippet: -## 13.05.12-4 - * fix not being able to properly cd to relative parent directories + - `defer func() {}()` + - `defer f()` -## 13.05.12-3 - * add a basic `cd` command to 9o. see 9o `help` for documentation + - ReturnSnippet: -## 13.05.12-2 - * mg/sh now handless binary output - * mg/sh now accepts a string `Cmd.Input` that allows passing input the command + - `return` -## 13.05.12-1 - * improved GoSublime-next syntax highlighting. - see https://github.com/DisposaBoy/GoSublime/issues/245 + - MutexSnippet: `mu.|` suggests: -## 13.05.06-4 - * display 9o wd in a simplified manner - * impl 9o_instance setting: note: this does not work yet + - `mu.Lock(); defer mu.Unlock(); |` + - `mu.Lock(); |; mu.Unlock()` -## 13.05.06-3 - * add support for setting the 9o color scheme with `9o_color_scheme` - * fix completion being shown in gs-next tmlang when the cursor it at the end of the line + - PackageNameSnippet: -## 13.05.06-2 - * disable completion in gs-next: strings, runes, comments + - `package main; func main() { | }` -## 13.05.06-1 - * A new syntax definition has been created to fix all the short-comings of the existing - syntax highlighting. if you're interested in testing it, please take a look at - https://github.com/DisposaBoy/GoSublime/issues/245 + - DocSnippet: suggest function names, field names, etc. inside the corresponding _documentation_ comment -## 13.05.04-4 - * add new `9o_instance` and `9o_color_scheme`. note: these are not yet implemented - see https://github.com/DisposaBoy/GoSublime/issues/243 +- Fix the golang.Gocode{} reducer changing View.Pos -## 13.05.04-3 - * add new `lint_enbaled` and `linters` settings. note: the new linter has not yet been implemented - see https://github.com/DisposaBoy/GoSublime/issues/220 +- The `(Add)UnimportedPackages` feature no longer adds circular imports -## 13.05.04-2 - * removed setting: `margo_addr`, if you have a *MarGo* binary in your $PATH, delete it. +- Show the tasks animation after 500ms instead of 1s to (hopefully) make things feel more responsive -## 13.05.04-1 - * don't sort imports when adding/removing +- GOROOT and GOPATH are scanned in parallel at startup -## 13.05.01-2 - * fix mg9 request leak +- All packages in GOROOT and GOPATH are available for the unimported packages feature, not just those in GOROOT and the package cache. -## 13.05.01-1 - * give PATH preference to Go related bin directories - this has the side-effect that if you set e.g. GOROOT in your project settings(e.g. GAE), - then $GOROOT/bin/go should be found first, even if you have a normal Go binary at /usr/bin/gos +- Add better named alias `pkg-list` for `unimported-packages` and show the directory where the package is -## 13.04.27-1 - * fix failure to load GoSublime.tmLanguage in st3 +- API BREAKAGE: + Most users should not be affected, but there were some API changes/breakages. -## 13.04.24-1 - * fix gs.which treating directories `$PATH` named `go` as the `go` executable + - removed CursorScope.Any and CursorScope.All -## 13.04.21-1 - ** WARNING ** - ** - ** the linter system is being redone - ** this means comp_lint and all lint-related settings will be removed or renamed - ** see https://github.com/DisposaBoy/GoSublime/issues/220 - ** + - make CursorCtx.Pos the int offset instead of token.Pos - * only show calltip if the call is on the same line as the cursor: - this avoids displaying a calltip for fmt.Println() in the following snippet + - `CursorNode` and `ParseCursorNode` were removed - fmt.| - fmt.Println("done") + - `CursorCtx.Ctx` is no longer embedded -## 13.04.14-2 - * fix failing to find a calltip for b() in p.a(p.b()) +## 18.11.06 -## 13.04.14-1 - * calltips are now implemented in margo +- Fix `UnimportedPackages` support for stdlib packages in go1.10 and earlier versions. -## 13.04.13-1 - * pre-compile margo on update (before restart) - * detect the go binary's path instead of relying on python - * try to work-around odd scrolling in 9o +## 18.11.04 -## 13.04.01-1 - * when replaying unsaved views you are now able to navigate to src lines inside 9o +- API BREAKAGE: Rename `mg.Reducer.Reducer*` to `mg.Reducer.R*`. -## 13.03.31-2 - * add GOBIIN to PATH - * set default package snippet to `package main` if the filename is main.go + Most users should be unaffected. + If you've _called_ any of these methods _directly_, + you will need to rename the following method calls: -## 13.03.31-1 - * use relative paths when setting syntax files: should fix any errors about not being able to load e.g. 9o.hidden-tmLanguage + - ReducerLabel -> RLabel + - ReducerInit -> RInit + - ReducerConfig -> RConfig + - ReducerCond -> RCond + - ReducerMount -> RMount + - Reducerduce -> Rduce + - ReducerUnmount -> RUnmount -## 13.03.30-3 - * update gocode to https://github.com/nsf/gocode/commit/86e62597306bc1a07d6e64e7d22cd0bb0de78fc3 +- API BREAKAGE: mg.RunCmdData has been un-exported -## 13.03.30-2 - * restore py3k compat: execfile was removed +- The following fields in the `&golang.GoCode{}` and `&golang.GocodeCalltips{}` reducers are now ignored. -## 13.03.30-1 - * work-around show_call_tip hang when the file starts with a comment + - Source: this is now the default + - ProposeBuiltins: this is now the default + - ProposeTests: use `&golang.MarGocodeCtl{}` + - Autobuild: we now use the source code so there are no plans to implement this + - UnimportedPackages: this is now the default -## 13.03.29-4 - * impl a basic oom killer in MarGo. If MarGo's memory usage reaches 1000m, she? will die - you can configure this limit in the user settings ctrl+dot,ctrl+5 - e.g. to limit the memory use to 500m use: + See `&golang.MarGocodeCtl{}` (below). - "margo_oom": 500 +- Add support for 'unimported' packages. -## 13.03.29-3 - * add support for showing function call tip live in the status bar - to enable to add the setting: - "autocomplete_live_hint": true - to your user settings in ctrl+dot,ctrl+5 + - auto-completing `json.` will now try to import `encoding/json` + - known bugs: when adding the import, the view will scroll + - known limitation: we don't scan GOPATH and we don't support the vendor directory - note: the old keybinding ctrl+dot,ctrl+space works as normal + Use `NoUnimportedPackages` (below) to disable this. -## 13.03.29-2 - * properly detect when the source(about.py) changes - * notify the user of an update if the on-disk vesion differs from the live version +- Add support for preloading imported packages when a view is activated. -## 13.03.29-1 - * add bindings for default setting(ctrl+dot,ctrl+4) and user settings(ctrl+dot,ctrl+5) + - This aims to keep the cache warm to speed up auto-completion. -## 13.03.28-2 - * more python hacks + Use `NoPreloading` (below) to disable this. -## 13.03.28-1 - * make the sanity check output more verbose - * add key bindings for: - (replace ctrl with super on os x) - README.md: ctrl+dot,ctrl+1 - USAGE.md: ctrl+dot,ctrl+2 - run sanity check: ctrl+dot,ctrl+3 +* Add support for adding `unimported` packages to the file. -## r13.03.25-1 - * abort blocking calls(completion, fmt) early if the install stage isn't set to "done" + - Use `AddUnimportedPackages` (below) to enabled this -## r13.03.24-3 - * wait for mg9.install to finish before attempting to send any request to margo. - fixes a false-positive error about the mg binary being missing before installtion completes +* All the above can be configured using the `&golang.MarGocodeCtl{}` reducer -## a13.03.24-2 - * fix call to getcwdu(not in py3k) + ```Go + &golang.MarGocodeCtl{ + // whether or not to include Test*, Benchmark* and Example* functions in the auto-completion list + // gs: this replaces the `autocomplete_tests` setting + ProposeTests: false, -## r13.03.24-1 - * communicate a tag/version between gs and mg so the case where they're out-of-sync can be detected + // Don't try to automatically import packages when auto-compeltion fails + // e.g. when `json.` is typed, if auto-complete fails + // "encoding/json" is imported and auto-complete attempted on that package instead + // See AddUnimportedPackages + NoUnimportedPackages: false, + // If a package was imported internally for use in auto-completion, + // insert it in the source code + // See NoUnimportedPackages + // e.g. after `json.` is typed, `import "encoding/json"` added to the code + AddUnimportedPackages: false, -## r13.03.23-2 - * use getcwdu instead of getcwd in hopes of avoiding python issues + // Don't preload packages to speed up auto-completion, etc. + NoPreloading: false, -## r13.03.23-1 - * foreign platform binaries will no longer be cleaned up - e.g. where the current platform is linux-x64, a linux-x32 binary (gosublime.margo.r13.03.23-1.linux-x32.exe) - will not be cleaned up until you load st2 on linux-x32 + // Don't suggest builtin types and functions + // gs: this replaces the `autocomplete_builtins` setting + NoBuiltins: false, + }, + ``` -## r13.03.20-1 - * MarGo EXE name has changed to gosublime.margo.[VERSION].[platform]-[arch].exe - e.g. gosublime.margo.r13.03.20-1.linux-x64.exe +* Add new lang conststants for `mg.JSX`, `mg.TS`, `mg.TSX` and rename `R` to `Rlang` -## r13.03.16-2 - * use the first action on a line if an action is triggered in the wrong place - e.g. if there is a filename and an error message, clicking on the error message will - 9o will now try find the filename +* Don't treat an empty non-nil slice as matching in `LangIs()` and `ActionIs()` -## r13.03.16-1 - * add imports to the top of the block. this causes them to be a part of the first group of imports - in cases where imports are group by separating them with a space +* Fix an infinite loop when auto-completing inside packages with cyclic dependencies -## r13.03.03-1 - * reduce false-positives in gs.flag linter - * fix margo help/flags (remove gocode flags from the output) +## 18.10.06 -## r13.03.02-1 - * cap the number of concurrent requests margo will process +- restore support for running individual test functions by pressing `ctrl+.`,`ctrl+g` or `ctrl+shift + left/right-click` on the function declaration's name -## r13.03.01-1 - * add go/types (like the old gotype) linter - * disable go/types linter by default: - to enabled it, set an empty filter list in your user settings e.g. `"lint_filter": []` +- add support for having multiple builtins with the same name -## r13.02.24-1 - * add new setting `lint_filter`. see the default settings for documentation +- API breakage: + `mg.ExecRunFunc()` was replaced with the pattern `CmdCtx.WithCmd().Run()` + the former bypasses builtins so running `go install` has no linting support -## r13.02.09-1 - *impl 9o `hist` command +## 18.09.30 -## r13.02.08-2 - * add THANKS.md there are several other donors who weren't added since your donations were - done anonymously. I'd like to add you as well :) if you want your name added please let me know - either thank you all! +- Improve autocompletion scope detection -## r13.02.08-1 - * initial(incomplete) Sublime Text 3 support - * gsshell ui and gs_shell command removed - * anything that imported or used gs* commands is probably broken + - snippets should now the shown when there is a comment above the package statement + - completion should no longer be shown when there is no package statement -## r13.02.03-3 - * impl `go share` as 9o command `share`. see ctrl+9 "help" for more details +- misc tweaks to the method snippets + - for pointer method receivers, only the `*` is selected for the initial method definition + - when there are syntax errors in the file, methods should no longer be suggested for the invalid type `_` -## r13.02.03-2 - * add new setting `build_command` to allow changing what command is run when you press ctrl+dot,ctrl+b - see the default settings for documentation (ctrl+dot,ctrl+dot "default settings") +## 18.09.25 -## r13.02.03-1 - * fmt verbs are now highlighted in raw strings - * fix race between fmt_save and 9o - * allow action'ing (super/ctrl+g only) seletions in 9o +- Switch golang.Gocode and golang.GocodeCalltips to new mode SrcImporterWithFallback by default -## r13.01.27-2 - * (by default) only save 9o history if the command was manually executed + This should improve the experience a lot: -## r13.01.27-1 - * correctly handle hist indexing when there's only one command in the history (last command not recalled on ctrl+dot,ctrl+b) + - in the old `Source: true` mode, CGO packages often failed + - in the old `Source: false` mode, you had to make sure the package was installed + and up-to-date + - in this new mode, we try the more reliable source mode and fallback + to the binary mode if it fails -## r13.01.26-1 - * fix broken package snippet (inserting blank package name) + As a result, the `Source: bool` fields are now ignored. + To restore the old behaviour, use the golang.MarGocodeCtl reducer: -## r13.01.25-2 - * set .go files to use the `GoSublime` syntax definition instead of `Go` as it's more complete - * hide GsDoc and 9o sytax definitions from the command palette + ```Go + &golang.MarGocodeCtl{ + ImporterMode: golang.SrcImporterOnly, + // or + ImporterMode: golang.BinImporterOnly, + } + ``` -## r13.01.25-1 - * fix 9o command history indexing (caused wrong command to be expanded for ^1, ^2 etc) +- replace margocodectl `cache-list-by-key` and `cache-list-by-dur` with `cache-list` + see `margocodectl cache-list --help` -## r13.01.24-2 - * add $HOME/go/bin to $PATH +- Improve FmtCmd's error message -## r13.01.24-1 - * add $HOME/bin to $PATH + When goimports fails due to a syntax error, the parse error should now be shown as well + and not just the meaningless `exit 2` error message -## r13.01.23-1 - * fix broken 9o-related keybindings (ctrl+dot,ctrl+r etc.) +## 18.09.18 -## r13.01.22-1 - * fix missing declarations in unsaved files +- fix a case where margo exits due to IPC shutdown + _you will need to restart Sublime Text_ +- return all possible completions in gocode to allow the editor to do filtering. + this restores the old behaviour where typing `abc.X` proposes `abc.XYX123` -## r13.01.21-1 - **majour refactoring - watch out for bugs** +## 18.09.14 - * fix handling of binary data in the run/replay commands - * misc tweaks+fixes - * remove gsdepends - * remove all rpc calls to margo.py - * remove margo0 +- This release adds a new experimental update notifier. -## r13.01.20-1 - **IMPORTANT** - this update marks the complete transition of all keybindings away from GsShell. - `ctrl+b` `ctrl+dot`,`ctrl+b` `ctrl+dot`,`ctrl+t` and `ctrl+dot`,`ctrl+r` - all uses 9o now. for more information about the GsShell replacement 9o please press ctrl+9 and type help + MOTD keeps you updated about new versions and important announcements -## r13.01.19-2 - **NOTICE** - The transition to 9o has begun. press ctrl+9 or super+9 and type `help` for more details on 9o. - 9o will evntually completely replace all GoSublime's interaction with the OS' shell. - This includes GsShell(ctrl+dot,ctrl+b). + It adds a new command `motd.sync` available via the UserCmd palette as `Sync MOTD` - As of this update, `ctrl+dot`,`ctrl+r` and `ctrl+dot`,`ctrl+t` has been remapped + `Interval` can be set in order to enable automatic update fetching. -## r13.01.19-1 - * impl 9o command history + When new updates are found, it displays the message in the status bar + e.g. `★ margo.sh/cl/18.09.14 ★` a url where you see the upcoming changes before updating -## r13.01.17-1 - * add keybindings in 9o for committing autocompletion instead of executing the prompt when auto_complete_commit_on_tab is false + It sends the following data to the url https://api.margo.sh/motd.json: -## r13.01.14-1 - * added pledgie badge http://www.pledgie.com/campaigns/19078 + - current editor plugin name e.g. `?client=gosublime` + this tells us which editor plugin's changelog to check + - current editor plugin version e.g. `?tag=r18.09.14-1` + this allows us to determine if there any updates + - whether or not this is the first request of the day e.g. `?firstHit=1` + this allows us to get an estimated count of active users without storing + any personally identifiable data -## r13.01.12-1 + No other data is sent. For more info contact privacy at kuroku.io - **WARNING** + To enabled it, add the following reducer: - GoSublime will soon switch to 9o `ctrl+9` or `super+9`. - It will replace GsShell `ctrl+dot`,`ctrl+b` (maybe `ctrl+b`). - GsShell has reached its EOL and as a result no GsShell specific bugs will be fixed, old or new. - The code (gsshell.py) will remain for a little while so if you use code that interacts - with it, now is the time to make let me know so necessary features can implemented in 9o + ```Go + &mg.MOTD{ + // Interval, if set, specifies how often to automatically fetch messages from Endpoint + // Interval: 3600e9, // automatically fetch updates every hour + }, + ``` -## r13.01.06-1 - * add two new 9o command `env` and `settings` see 9o `help` for more details - * 9o now supports a new scheme `gs.packages` e.g. `ctrl+shft`, left-click on gs.packages://GoSublime/9o.md will open the 9o docs + You will need to restart Sublime Text. + Unless you uncomment/set `Interval`, you will need to manually check for updates + using the `Sync MOTD` command from the usercmd palette + `ctrl+.`,`ctrl+c` / `super+.`,`super+c` -## r13.01.05-2 - * added two task aliases to tskill - `tskill replay` will kill/cancel the last replay command +- The `GoSublime: Go` syntax was switched to a new syntax based on the Go syntax shipped in Sublime Text - `tskill go` will kill the last go command (go test, etc.). as a consequence, - the 9o `go` command now acts like the `replay` command in that kills any previous instance + - if you find any breakages, please file an issue at margo.sh/gs/i + - if you prefer the colouring of the previous version, you can switch back to the old syntax + via `Menu > View > Syntax > Open all with current extension as... > GoSublime > GoSublime: Go (Deprecated)` + please not that this version is buggy and will not receive any fixes - * added new setting autosave: - controls whether or not pkg files should be automatically saved when necessary - (e.g. when running 9o `replay` or `go test` commands) +- golang.Gocode, golang.GocodeCalltips: -## r13.01.05-1 - * impl click-tests. i.e `ctrl+shift`,`left-click` on words that start with Test,Benchmark or Example - will run go corresponding test or bench. `ctrl+shift`,`right-click` will do the same but using only the prefix - e.g. - `ctrl+shift`,`left-click` on `BenchmarkNewFunc` will run only `BenchmarkNew`: - `go test -test.run=none -test.bench="^BenchmarkNew$"` + - reduce memory use with `Source: true` + - support syscall/js - `ctrl+shift`,`right-click` on `BenchmarkNewFunc` will run all benchmarks: - `go test -test.run=none -test.bench="^Benchmark.*"` +- golang.Guru gained support for syscall/js + guru is now called with `-tags "js wasm"` if `syscall/js` is imported in the package -## r12.12.29-1 - * impl 9o tskill command. see 9o(ctrl+9) "help" for more info +## 18.08.31 -## r12.12.27-2 - * impl `go test` in 9o run and replay +- Switch the `ctrl+.`,`ctrl+t` / `cmd+.`,`cmd+t` keybinding to the new &golang.TestCmds{} reducer: -## r12.12.27-1 - * introducing 9o, the new command-shell. press `ctrl+9` or `super+9` to activate it. - WARNING: in the near future 9o will replace GsShell + ```Go + &golang.TestCmds{ + // additional args to add to the command when running tests and examples + TestArgs: []string{}, -## r12.12.26-1 - * sync gocode: Windows-specific config_dir/config_file implementation. + // additional args to add to the command when running benchmarks + BenchArgs: []string{"-benchmem"}, + }, + ``` -## r12.12.13-2 - * add a new setting: `autocomplete_filter_name` - you may set this to a regexp which will be used to filter entries in the auto-completion list - e.g. `"autocomplete_filter_name": "^autogenerated_"` will prevent any type or function - whose name begins with "autogenerated_" from appearing in the auto-completion list +## 18.08.29 -## r12.12.13-1 - * implement `9 replay` command that will `9 play` (build + run) the current package, after killing any previous instances. - Until it goes live, you can override the existing `ctrl+dot`,`ctrl+r` binding or bind it to something else by adding - the following key binding to your user key bindings via menu `Preferences > Key Bindings - User` +- implement more aggressive gocode caching. + behind the scenes, imported/type-checked packages are cached until the respective package is edited. - { - "keys": ["ctrl+.", "ctrl+r"], - "command": "gs_commander_open", - "args": {"run": ["9", "replay"]}, - "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] - } + - it should now be ok to use `Source: true` option without slowdowns. + - as a bonus, `go modules` should now have completion with `Source: true` + - please note that `Source: true` uses a lot more memory (see below for details about cache pruning) + - if both &golang.Gocode{Source: true} and &golang.GocodeCalltips{Source: true} + use `Source: true`, they will share the cache (less memory use) -## r12.12.2-3 - * setting `margo_cmd` has been removed - -## r12.12.2-2 - * setting `gocode_cmd` has been removed - -## r12.12.2-1 - * setting `complete_builtins` has been renamed to `autocomplete_builtins` +- add new reducer `&golang.MarGocodeCtl{}` + this allows manual cache management using the new `margocodectl` command -## r12.11.28-1 - * If you have issues with env vars, particularly on OS X, consider setting the - `shell` setting. See `Packages/User/GoSublime.sublime-settings` for more details + - to clear the cache use the command `margocodectl cache-prune` + run `margocodectl` for info about how to use the command + - automated cache pruning will be implemented in the future -## r12.11.15-1 - * MarGo (margo0) and gocode are now bundled with GoSublime and should be in active use. - Feel free to remove any old source from $GOPATH*/github.com/{nsf/gocode,DisposaBoy/MarGo} - if you have no use for them in additiion to their respective binaries +## 18.08.22 -## r12.11.04-1 - * added new setting `complete_builtins` - set this to `true` to show builtin type and functions in the completion menu +- merge all shell env vars named `^(MARGO|GO|CGO)\w+` into the GoSublime environment + this ensures new env vars like `GOPROXY` and `GO111MODULE` work correctly -## r12.11.03-1 - * BREAKING CHANGES ARE COMING: in the next GoSublime update support for windows-style - environment variables will be removed. - If you have environment variables that are not expanded before GS sees them and they are - of the form `%HOME%`, `%GOPATH%` etc. they will no longer be expanded. - You should transition to *nix-style env vars. +- try to prevent `GO111MODULE` leaking into the agent build process - i.e `%GOPATH%` etc. should be changed to `$GOPATH`. `$$` can be used to escape to escape`$` characters +- add support for UserCmd prompts -## r12.09.22-1 - * the experimental gsshell replacement codename shelly is no more. - it has been replaced with gscommander which operates from within the output panel - a separate instance per directory - - to activate it press `ctrl+#` or `super+#` - proper documentation will appear as things progress but for now it works as follows: - paths are highlighted(usually bold), pressing `ctrl+shift+[left click]` will open it. - if the path is a file it will open in ST2 otherwise if it's a url it will be opened - in your web browser - - typing `#` followed by a command and pressing `enter` will run that command - - auto-completion and implementation of common commands such as `cd` and `go play` will follow soon - - -## r12.09.16-1 - * add typename-aware method definition snippets for types declared in the current file - -## r12.09.08-2 - * add new setting `comp_lint_commands` that allows you specify what commands comp-lint should run - e.g to run `go vet` followed by `go install`, add the following to your user settings. - by default the only command run for comp-lint is `go install` - - "comp_lint_enabled": true, // enable comp-lint - "comp_lint_commands": [ - {"cmd": ["go", "install"]}, // first run go install - {"cmd": ["go", "vet"]} // followed by go vet, - ], - "on_save": [ - {"cmd": "gs_comp_lint"} // setup comp-lint to run after you save a file - ], - - see `Package/GoSublime/GoSublime.sublime-settings` for details - -## r12.09.08-1 - * add support snippets (an alternative to Sublime Text's Native snippets) - see `Package/GoSublime/GoSublime.sublime-settings` for details - -## r12.08.26-1 - * make gs_browse_files (`ctrl+dot`,`ctrl+m`) act more like a file browser. - it now lists all files in the current directory tree excluding known binary files: - (.exe, .a, files without extension, etc.) and an entry to go to the parent directory - -## r12.08.23-1 - * add experimental support for post-save commands - a new entry `on_save` is supported in `GoSublime.sublime-settings`, it takes a list of commands - in the form of an object {"cmd": "...", "args": {...}} where cmd can be any TextCommand - * add experimental support for `go install` on save in the form of another linter. - to activate it add the following to your `GoSublime.sublime-settings` - - "comp_lint_enabled": true, - "on_save": [ - {"cmd": "gs_comp_lint"} - ] - - note: enabling this will override(disable) the regular GsLint - -## r12.08.10-3 - * `ctrl+dot`,`ctrl+a` is now accessible globally - -## r12.08.10-2 - * `ctrl+dot`,`ctrl+o` now presents a file list instead of opening a file - -## r12.08.10-1 - * `ctrl+dot`,`ctrl+m` now list all relevant files (.go, .c, etc.) - as well all files in the directory tree recursively (sub-packages) - it also now works globally - -## r12.08.08-1 - * fix a bug which could cause MarGo to take a long time to respond (when accidentally parsing binary files) - update MarGo - -## r12.07.31-1 - * add platform info e.g (linux, amd64) to pkg declarations list (`ctrl+dot`,`ctrl+l`) - -## r12.07.28-2 - * add command palette entry to show the build output - press `ctrl+dot`,`ctrl+dot` and start typing `build output` - -## r12.07.28-1 - * update gocode: nsf fixed a bug that could cause gocode to hang on invalid input - -## r12.07.21-2 - * fix: handle filename for browse files correctly - update MarGo - -## r12.07.21-1 - * add support for browsing/listing the files in a the current package - press `ctrl+dot`,`ctrl+m` - update MarGo - -## r12.07.15-2 - * add basic call-tip? support - * press `ctrl+dot`,`ctrl+space` inside a function parameter list to show its declaration - -## r12.07.15-1 - * update gocode: nsf recently added improved support for variables declared in the head of `if` and `for` statements - -## r12.07.12-1 - * fix: imports not sorted on fmt/save - * fix: GsDoc doesn't work correctly in unsaved files - * various presentation tweaks - * documentation comments are now displayed for types - * package documentation is now displayed - * goto definition of packages is now enabled - * various keybindings now available in non .go files - `ctrl+dot`,`ctrl+dot` - open the command palette with only GoSublime entries - `ctrl+dot`,`ctrl+n` - create a new .go file - `ctrl+dot`,`ctrl+o` - browse packages - * update MarGo - -## r12.07.08-2 - * new quick panel for go test - allows easily running `Test.*`, `Example.*`, `Benchmark.*` or individual tests, examples and benchmarks - press `ctrl+dot`,`ctrl+t` to access the quick panel - -## r12.07.08-1 - * you can now browse packages - press `ctrl+dot`,`ctrl+o` to open the first file found in the select pkg dir - * new key binding added `ctrl+dot`,`ctrl+l` to list the declarations in the current pkg in a single step - it does the same thing as `ctrl+dot`,`ctrl+a` and then selecting 'Current Package' - -## r12.07.07-2 - * you can now browse declarations in the current package(beyond file-scope) - as well as all other packages - press `ctrl+dot`,`ctrl+a` to browser packages via a quick panel - listing the declarations in the current is still `ctrl+dot+`,`ctrl+d` - * update MarGo - -## r12.07.07-1 - * improve GsLint detection of un-called flag.Parse() - * listing declarations now works in unsaved files - * please update MarGo - -## r12.06.29-2 - * GsDoc documentation now shows example functions and blocks are now collapsed - * update MarGo - -## r12.06.29-1 - * fix: threading that caused gslint to crash - * - * added initial support for per-project settings - * a settings object named `GoSublime` in your project settings will override values - * specified in the `Gosublime.sublime-settings` file - * - * added new dynamic pseudo-environment variable `GS_GOPATH` will contain an auto-detected GOPATH - * e.g. if you file name is `/tmp/go/src/hello/main.go` it will contain the value `/tmp/go` - * it can safely added to your regular `GOPATH` `env` setting e.g. - * `"env": { "GOPATH": "$HOME/go:$GS_GOPATH" }` - * this allows for seemless use of project-based GOPATHs without explicit configuration - * - * added ctrl+click binding for GsDoc - * `ctrl+shift+left-click` acts as alias for `ctrl+dot,ctrl+g` a.k.a goto definition - * `ctrl+shift+right-click` acts as alias for `ctrl+dot,ctrl+h` a.k.a show documentation hint - * as always, `super` replace `ctrl` on OS X - -## r12.06.26-2 - * GsDoc now supports local, package-global and imported package variables and functions - (MarGo/doc is still incomplete, however: types(structs, etc.) are not resolved yet) - I've changed the way GsDoc works. Both mode are unified, ctrl+dot,ctrl+g will take you to - the definition but the hint( ctrl+dot,ctrl+h ) now displays the src along with any comments - attached to it (this is usually pure documentation) - * MarGo needs updating - -## r12.06.26-1 - * fix: file saving in gsshell - * fix: duplicating comment that follows imports when imports are modified - * fix: adding duplicate entries to the package list due to filename case-insensitivity - * the new_go_file command now automatically fills out the package declaration - * add binding to create a new go file ( ctrl+dot,ctrl+n ) - -## r12.06.17-1 - * add support for running(play) the current file without saving it (`ctrl+dot`, `ctrl+r`) - * add support for sharing the contents of the current on play.golang.org - press `ctrl+dot`, `ctrl+dot` for a list of all commands and their key bindings as well sharing functionality - -## r12.06.09-2 - * MarGo now supports warning about calling flag.String() etc and forgetting to call flag.Parse() afterwards - -## r12.06.09-1 - * removed ctrl+shift+g keybinding, please use `ctrl+dot`,`ctrl+dot` to show the list of available commands and their kebindings - * complete implementation of imports: - use `ctrl+dot`,`ctrl+p` to add or remove packages - use `ctrl+dot`,`ctrl+i` to quickly jump to the last imported package where you can assign an alias, etc. - use `ctrl+dot`,`ctrl+[` to go back to where you were before - * MarGo needs updating and a restart of ST2 is recommended - -## r12.06.05-1 - * add support for configuring the fmt tab settings - see GoSublime.sublime-settings (fmt_tab_width and fmt_tab_indent) - -## r12.06.02-1 - * Add initial stub implementation of goto-definition and show-documentation - * this requires the latest version of MarGo - * new key bindings and commands: press `ctrl+.`, `ctrl+.` - * (control or super on OS X, followed by .(dot) twice) - * or open the command palette(`ctrl+shift+p`) and type `GoSublime:` - * to show a list of available commands and their respective key bindings - * note: currently only the pkgname.Function is supported, so types, methods or constants, etc. - -## r12.05.30-1 - * fix completion only offering the 'import snippet' if there aren't any imports in the file - -## r12.05.29-1 - * update MarGo - -## r12.05.26-2 - * re-enable linting - -## r12.05.26-1 - * start using margo.fmt, no more dependecy on `gofmt` and `diff` - -## r12.05.05-1 - * add support for installing/updating Gocode and MarGo + this enables the creation of UserCmds like the following, without dedicated support from margo: + + ```Go + mg.UserCmd{ + Title: "GoRename", + Name: "gorename", + Args: []string{"-offset={{.View.Filename}}:#{{.View.Pos}}", "-to={{index .Prompts 0}}"}, + Prompts: []string{"New Name"}, + } + ``` + +- fix #853 a build failure when using snap packaged go1.10 + +- fix caching of packages in GOPATH when doing gocode completion + this _might_ slow completion, but there should no longer be any stale non-GOROOT package completions + +- add new `Source` option to use source code for gocode completions + _this will most likely be very slow_ + + ```Go + &golang.Gocode{ Source: true } + &golang.GocodeCalltips{ Source: true } + ``` + +## 18.08.15 + +- fix missing `go` command integration by default + +- you may need to add the reducer `&golang.GoCmd{}` + +- this adds new commands (callable through 9o): + + - `go`: Wrapper around the go command, adding linter support + + - `go.play`: Automatically build and run go commands or run go test for packages + with support for linting and unsaved files + + - `go.replay`: Wrapper around go.play limited to a single instance + by default this command is bound to `ctrl+.,ctrl+r` or `cmd+.,cmd+r` + + UserCmds (`ctrl+.,ctrl+c` / `cmd+.,cmd+c`) are also added for `Go Play` and `Go RePlay` diff --git a/Default (Linux).sublime-keymap b/Default (Linux).sublime-keymap index 24fae8e6..b4fdca90 100644 --- a/Default (Linux).sublime-keymap +++ b/Default (Linux).sublime-keymap @@ -49,6 +49,10 @@ "keys": ["ctrl+.", "ctrl+e"], "command": "margo_issues", }, + { + "keys": ["ctrl+.", "ctrl+c"], + "command": "margo_user_cmds", + }, { "keys": ["ctrl+.", "ctrl+["], "command": "gs_palette", @@ -70,12 +74,11 @@ "keys": ["ctrl+.", "ctrl+r"], "command": "gs9o_open", "args": {"run": ["replay"], "focus_view": false}, - "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] }, { "keys": ["ctrl+.", "ctrl+g"], - "command": "gs_doc", - "args": {"mode": "goto"}, + "command": "gs9o_open", + "args": {"run": [".actuate"], "focus_view": false, "show_view": false}, "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] }, { @@ -122,8 +125,8 @@ }, { "keys": ["ctrl+.", "ctrl+t"], - "command": "gs_test", - "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] + "command": "margo_user_cmds", + "args": {"action": "QueryTestCmds"}, }, { "keys": ["ctrl+.", "ctrl+space"], @@ -132,7 +135,15 @@ }, { "keys": ["ctrl+9"], - "command": "gs9o_open" + "command": "gs9o_win_open" + }, + { + "keys": ["ctrl+.","ctrl+9"], + "command": "gs9o_win_open" + }, + { + "keys": ["ctrl+.","ctrl+0"], + "command": "margo_show_hud" }, { "keys": ["ctrl+space"], diff --git a/Default (OSX).sublime-keymap b/Default (OSX).sublime-keymap index 3b42fab6..acc41b23 100644 --- a/Default (OSX).sublime-keymap +++ b/Default (OSX).sublime-keymap @@ -41,9 +41,12 @@ }, { "keys": ["super+.", "super+e"], - "command": "gs_palette", "command": "margo_issues", }, + { + "keys": ["super+.", "super+c"], + "command": "margo_user_cmds", + }, { "keys": ["super+.", "super+["], "command": "gs_palette", @@ -65,12 +68,11 @@ "keys": ["super+.", "super+r"], "command": "gs9o_open", "args": {"run": ["replay"], "focus_view": false}, - "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] }, { "keys": ["super+.", "super+g"], - "command": "gs_doc", - "args": {"mode": "goto"}, + "command": "gs9o_open", + "args": {"run": [".actuate"], "focus_view": false, "show_view": false}, "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] }, { @@ -117,8 +119,8 @@ }, { "keys": ["super+.", "super+t"], - "command": "gs_test", - "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] + "command": "margo_user_cmds", + "args": {"action": "QueryTestCmds"}, }, { "keys": ["super+.", "shift+space"], @@ -127,7 +129,15 @@ }, { "keys": ["super+9"], - "command": "gs9o_open" + "command": "gs9o_win_open" + }, + { + "keys": ["super+.","super+9"], + "command": "gs9o_win_open" + }, + { + "keys": ["super+.","super+0"], + "command": "margo_show_hud" }, { "keys": ["shift+space"], diff --git a/Default (Windows).sublime-keymap b/Default (Windows).sublime-keymap index 6f27e236..4be25420 100644 --- a/Default (Windows).sublime-keymap +++ b/Default (Windows).sublime-keymap @@ -47,9 +47,12 @@ }, { "keys": ["ctrl+.", "ctrl+e"], - "command": "gs_palette", "command": "margo_issues", }, + { + "keys": ["ctrl+.", "ctrl+c"], + "command": "margo_user_cmds", + }, { "keys": ["ctrl+.", "ctrl+["], "command": "gs_palette", @@ -71,12 +74,11 @@ "keys": ["ctrl+.", "ctrl+r"], "command": "gs9o_open", "args": {"run": ["replay"], "focus_view": false}, - "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] }, { "keys": ["ctrl+.", "ctrl+g"], - "command": "gs_doc", - "args": {"mode": "goto"}, + "command": "gs9o_open", + "args": {"run": [".actuate"], "focus_view": false, "show_view": false}, "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] }, { @@ -123,8 +125,8 @@ }, { "keys": ["ctrl+.", "ctrl+t"], - "command": "gs_test", - "context": [{ "key": "selector", "operator": "equal", "operand": "source.go" }] + "command": "margo_user_cmds", + "args": {"action": "QueryTestCmds"}, }, { "keys": ["ctrl+.", "ctrl+space"], @@ -133,7 +135,15 @@ }, { "keys": ["ctrl+9"], - "command": "gs9o_open" + "command": "gs9o_win_open" + }, + { + "keys": ["ctrl+.","ctrl+9"], + "command": "gs9o_win_open" + }, + { + "keys": ["ctrl+.","ctrl+0"], + "command": "margo_show_hud" }, { "keys": ["ctrl+space"], diff --git a/GoSublime.py b/GoSublime.py index a151dcf7..afe86b71 100644 --- a/GoSublime.py +++ b/GoSublime.py @@ -22,16 +22,20 @@ print("GoSublime: %s" % execErr) def loadable_mods(): + from . import _before + from . import _after from .gosubl import gs from .gosubl import sh from .gosubl import margo from .gosubl import mg9 return [ + ('_before', _before), ('gs', gs), ('sh', sh), ('margo', margo), ('mg9', mg9), + ('_after', _after), ] def plugin_loaded(): diff --git a/GoSublime.sublime-commands b/GoSublime.sublime-commands index 8969f5c4..fcb26aec 100644 --- a/GoSublime.sublime-commands +++ b/GoSublime.sublime-commands @@ -26,6 +26,10 @@ "caption": "GoSublime: Show Issues", "command": "margo_issues", }, + { + "caption": "GoSublime: User Commands", + "command": "margo_user_cmds", + }, { "caption": "GoSublime: Go to last bookmark", "command": "gs_palette", @@ -60,8 +64,8 @@ }, { "caption": "GoSublime: Goto Definition", - "command": "gs_doc", - "args": {"mode": "goto"} + "command": "gs9o_open", + "args": {"run": ["goto.definition"], "focus_view": false, "show_view": false}, }, { "caption": "GoSublime: Show documentation hint", diff --git a/GoSublime.sublime-settings b/GoSublime.sublime-settings index d51d6333..deef0c28 100644 --- a/GoSublime.sublime-settings +++ b/GoSublime.sublime-settings @@ -30,10 +30,10 @@ "autosave": true, // Whether or not gscomplete(gocode) is enabled - "gscomplete_enabled": true, + "gscomplete_enabled": false, // Whether or not gsfmt is enabled - "fmt_enabled": true, + "fmt_enabled": false, // whether or not to indent with tabs (alignment is always done using spaces) "fmt_tab_indent": true, @@ -57,7 +57,7 @@ "ipc_timeout": 1, // Whether or not gslint is enabled - "gslint_enabled": true, + "gslint_enabled": false, // filter the kinds of lint checks that are done. supported kinds: // @@ -94,7 +94,7 @@ // Not Implemented // Whether or not gslint is enabled - "lint_enabled": true, + "lint_enabled": false, // Not Implemented // list of linters to run @@ -181,7 +181,7 @@ // whether or not to show function call tip in the status bar // the same can be achieved ctrl+dot,ctrl+space using an output panel - "calltips": true, + "calltips": false, // whether or not to use named imports when the basename of the import path doesn't match the pkg name // e.g. gosubli.me/go-foo would be imported as: @@ -280,7 +280,9 @@ // if set, 9o will run in single-instance mode instead of per-pkg // the name can be any string, so you can e.g. set it per-project and maintain project-specific // command history - "9o_instance": "", + // + // setting it to the string "auto" will automatically assign it a name based on the current directory/package + "9o_instance": "9o", // if set 9o will use the specified color scheme. // the path must relative to `Packages` e.g. `Packages/My/9o Specific.tmTheme` @@ -310,14 +312,12 @@ // "9o_aliases": {}, - // what 9o command to run when (super or )ctrl+dot,ctrl+b us pressed + // what 9o command to run when (super or )ctrl+dot,ctrl+b is pressed // e.g. ["go", "build"] // the 9o command ^1 recalls the last command you ran manually // see 9o help(ctrl+9 "help") for more details about what commands are supported "build_command": ["^1"], - "auto_complete_triggers": [ {"selector": "source.go", "characters": "."} ], - // exclude files with the listed prefixes from the file browsing palette (ctrl+dot,ctrl+m) "fn_exclude_prefixes": [".", "_"], @@ -325,7 +325,7 @@ // `GoSublime: HTML` files are html files with the template delimiters `{{` and `}}` tailored to // Go templates (text/template, html/template) // (`.gohtml` files are automatically set by the syntax definition) - "gohtml_extensions": [".html.go"], + "gohtml_extensions": [], // Export the listed environment variables to Sublime Text when the GoSublime settings change // so the env vars GS loads through your shell and project are available to other plugins diff --git a/Preferences.sublime-settings b/Preferences.sublime-settings index 7630fb82..37716af6 100644 --- a/Preferences.sublime-settings +++ b/Preferences.sublime-settings @@ -1,3 +1,3 @@ { - "auto_complete_triggers": [ {"selector": "source.go", "characters": "."} ] -} \ No newline at end of file + "auto_complete_triggers": [ {"selector": "source.go - comment - string", "characters": "."} ], +} diff --git a/README.md b/README.md index 7d27538e..d80431ab 100644 --- a/README.md +++ b/README.md @@ -1,105 +1,53 @@ - +# GoSublime [![Backers on Open Collective](https://opencollective.com/gosublime/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/gosublime/sponsors/badge.svg)](#sponsors) [![Build Status](https://travis-ci.org/DisposaBoy/GoSublime.svg?branch=master)](https://travis-ci.org/DisposaBoy/GoSublime) -

+## Intro +GoSublime is an IDE-like plugin for [Sublime Text 3](http://www.sublimetext.com/) mainly, but not limited to, providing integration for most of your Go/Golang development tools. -[![Backers on Open Collective](https://opencollective.com/gosublime/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/gosublime/sponsors/badge.svg)](#sponsors) [![Build Status](https://travis-ci.org/DisposaBoy/GoSublime.svg?branch=master)](https://travis-ci.org/DisposaBoy/GoSublime) -
+See https://margo.sh/b/hello-margo/ for a brief introduction to margo, the engine behind GoSublime. -GoSublime -========= +## Installation & Support +See https://margo.sh/b/migrate/ for instructions on how to install GoSublime. +See [SUPPORT.md](SUPPORT.md) for details about what level of support you can expect while using GoSublime. -Intro ------ +## Features -GoSublime is a Golang plugin collection for the text editor [Sublime Text](http://www.sublimetext.com/) providing code completion and other IDE-like features. Only Sublime Text **3** is supported. +- code completion from Gocode (fork); +- context aware snippets via the code-completion popup; +- sublime build system(ctrl+b) integrating with GoSublime 9o command prompt with live command output; +- lint/syntax check as you type or on save; +- quickly jump to any linter error reported in any open file or package; +- quickly fmt your source or automatically on save to conform with your coding standards; +- easily create a new go file and run it without needing to save it first (9o `replay`); +- share your snippets (anything in the loaded file) on play.golang.org; +- list declarations in the current file or package; +- automatically add/remove package imports; +- quickly jump your import section(automatically goes to the last import) where you can easily edit the pkg alias and return to where you were before; +- go to definition of a package function or constant, etc.; +- create your own margo extensions in Go to e.g. add context-aware commands to the command palette. -Before using GoSublime you should read and understand [SUPPORT.md](https://github.com/DisposaBoy/GoSublime/blob/master/SUPPORT.md) +## Demo -Features --------- +- Old demo http://vimeo.com/disposaboy/gosublime-demo2 -* code completion from [Gocode](https://github.com/nsf/gocode) -* context aware snippets via the code-completion popup to complement the existing SublimeText Go package. -* sublime build system(ctrl+b) integrating with GoSublime 9o command prompt -* lint/syntax check as you type -* quickly jump to any syntax error reported (and jump back to where you were before (across files)) -* quickly fmt your source or automatically on save to conform with the Go standards -* easily create a new go file and run it without needing to save it first (9o `replay`) -* share your snippets (anything in the loaded file) on play.golang.org -* list declarations in the current file -* automatically add/remove package imports -* quickly jump your import section(automatically goes to the last import) where you can easily edit the pkg alias and return to where you were before -* go to definition of a package function or constant, etc. -* show the source(and thus documentation) of a variable without needing to change views +## Copyright, License & Contributors -Demo ----- - -* Old demo http://vimeo.com/disposaboy/gosublime-demo2 - -![](https://github.com/DisposaBoy/GoSublime/raw/master/ss/2.png) -![](https://github.com/DisposaBoy/GoSublime/raw/master/ss/1.png) - -Installation ------------- - -It is assumed that you have a working installation of [Git](https://git-scm.com/) and know how to use it to clone and update repositories. - -Run the command `git clone https://github.com/DisposaBoy/GoSublime` from within the Sublime Text `Packages` directory. -The location of your Sublime Text Packages directory can be found by clicking the menu: `Preferences` > `Browse Packages...`. - -Usage ------ - -Please see [USAGE.md](USAGE.md) and [9o.md](9o.md) for general usage and other tips for effective usage of GoSublime - -**NOTE** GoCode is entirely integrated into GoSublime/MarGo. If you see any bugs related to completion, -assume they are GoSublime's bugs and I will forward bug reports as necessary. - -Settings --------- - -You can customize the behaviour of GoSublime by creating a settings file in your `User` package. This can be accessed from within SublimeText by going to the menu `Preferences > Browse Packages...`. Create a file named `GoSublime.sublime-settings` or alternatively copy the default settings file `Packages/GoSublime/GoSublime.sublime-settings` to your `User` package and edit it to your liking. - -Note: File names are case-sensitive on some platforms (e.g. Linux) so the file name should be exactly `GoSublime.sublime-settings` with capitalization preserved. - - -Copyright, License & Contributors -================================= - -GoSublime and MarGo are released under the MIT license. See [LICENSE.md](LICENSE.md) - -GoSublime is the copyrighted work of *The GoSublime Authors* i.e me ([https://github.com/DisposaBoy/GoSublime](DisposaBoy)) and *all* contributors. If you submit a change, be it documentation or code, so long as it's committed to GoSublime's history I consider you a contributor. See [AUTHORS.md](AUTHORS.md) for a list of all the GoSublime authors/contributors. +margo and GoSublime are released under the MIT license. See [LICENSE.md](LICENSE.md) Thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. -Supporters -========== - -GoSublime has received support from many kind individuals and as a thank you I've added most to [THANKS.md](THANKS.md) file as a way of saying *Thank You*. Some donors donated anonymously and so are not listed, however. If you have donated and would like to add an entry to this file, feel free to open a pull request. - -Donations -========= - -Supporting me means I can spend more time working on GoSublime and other Open Source projects, hopefully leading to more consistent and regular development. - -Donate using Liberapay - -Donate using Liberapay - - - -Donate using PayPal - -Donate using PayPal +### Supporters +GoSublime has received support from many kind individuals and as a thank you I've added most to [THANKS.md](THANKS.md) file as a way of saying _Thank You_. Some donors donated anonymously and so are not listed, however. If you have donated and would like to add an entry to this file, feel free to open a pull request. +### Donations +See https://margo.sh/funding/ for ways in which you can help support future development of margo and GoSublime. +
Become a backer or a sponsor on OpenCollective @@ -109,7 +57,6 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com - ### Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gosublime#sponsor)] @@ -125,7 +72,6 @@ Support this project by becoming a sponsor. Your logo will show up here with a l -
- + diff --git a/SUPPORT.md b/SUPPORT.md index a5596063..1173111f 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,62 +1,57 @@ -This document aims clarify what level of support you can expect when using GoSublime. -Use of GoSublime assumes you've read and understood *all* the points herein. +# Support -Discussion of support and this file in particular are tracked here: https://github.com/DisposaBoy/GoSublime/issues/689 +This document aims to clarify what level of support you can expect while using GoSublime. +Use of GoSublime assumes you've read and understood _all_ the points herein. +## Sublime Text -# Sublime Text +- All versions of Sublime Text **3** should be supported. +- For versions before the official 3.0 release in September 2017, graceful fall-backs are in place. +- Testing is only done for the current non-beta version and only on Linux. -* **Sublime Text 2 is *not* supported.** -* It's several years old at this point and Sublime HQ does not respond to my support requests. -* Furthermore, they've changed the default download link to Sublime Text 3 implying they do not support it either. +## Experience -If you have a *good* reason to not upgrade to Sublime Text 3, -discuss it here https://github.com/DisposaBoy/GoSublime/issues/689 +- It is assumed that you are experienced with Sublime Text, basic key bindings, its settings system, etc. +- It is assumed that you already have a working Go installation: https://golang.org/doc/install. +## Package Control +- Package Control is not supported -# Experience +## Go -* It is assumed that you are experienced with Sublime Text, basic key bindings, its settings system, etc. -* It is assumed that you already have a working Go installation: https://golang.org/doc/install -* You are expect to have read and understand the contents of the files: GoSublime.sublime-settings, USAGE.md and 9o.md +GoSublime is backed by https://margo.sh/ to which the following points apply: -# Sublime Text's Go package +- Like the official Go [release policy](https://golang.org/doc/devel/release.html#policy), only the current and previous released versions of Go are supported. +- Only the main `gc` tool-chain distributed by https://golang.org/ is supported. +- margo should not require a cgo-enabled Go installation, but non-cgo builds i.e. `CGO_ENABLED=0` are not tested. -* I disable the built-in Go package so I do not test for compatibility or conflicts with GoSublime. +## Go Modules -# Package Control +At this time, Go modules are only partially supported. +Auto-completion and other features built directly into margo should work in most cases, +but features backed by external tools e.g. `goto definition` might work. -* I do not use Package Control and therefore not able to offer support for any issue related to it. -* As a user, *you* are expected take care when updating GoSublime. -* You are advised *not* to utomatically update GoSublime. +We plan to implement most of these features internally as our (type-check) importer matures. -# Go +## Operating Systems -Please note that GoSublime is backed by a Go program named MarGo to which the following points apply. +- Testing is only done on Arch Linux. +- Windows and macOS should work without issue, but no testing is done on them. -* The minimum supported version of Go is go1.6. -* Older versions of Go might be able to compile MarGo without issue, but I will not test these older versions. -* I also do not test the gccgo, llvm, etc. tool-chains. Only the main `gc` tool-chain is supported. -* MarGo should not require a cgo-enabled Go installation, but I do not test installations with it disabled. +## Tools -# Operating Systems +Please note: -* I only test Linux. -* Windows and OS X should work without issue, but I do *not* test anything on them. +- By default `fmt` is achieved through direct use of the packages in the stdlib and not the binaries on your system. -# Tools +## Sponsors & Backers -Please note: +While we will make an effort to respond to all issues, we have only a limited amount of time and have chosen to give higher priority to our sponsors and backers (including those who donate outside of Open Collective and Patreon). -* GoSublime uses its own fork of `gocode` so any installation on your system is ignored. -* By default `fmt` is achieved through direct use of the packages in the stdlib and not the binaries on your system. +If an urgent response is required, or an issue has gone without response for more than a few days, our sponsors and backers are welcome to send an email to support@margo.sh. -I do not use the following tools and do *not* test for compatibility with them: +## Issues with sensitive details -* GVM or any other Go version manager -* GB or any other other alternative to the `go` tool -* `goimports`, the `gofmt`/`go fmt` *binary* or any other `gofmt` alternative -* If you use the `fmt_cmd` setting with `goimports` or any other slow command - you should read and understand the `ipc_timeout` setting documented in `GoSublime.sublime-settings` +If your issue contains sensitive details or you would otherwise prefer not to post it publicly, you're welcome to send an email to support@margo.sh. diff --git a/_after.py b/_after.py new file mode 100644 index 00000000..47858a87 --- /dev/null +++ b/_after.py @@ -0,0 +1,3 @@ + +def gs_init(m={}): + pass diff --git a/_before.py b/_before.py new file mode 100644 index 00000000..47858a87 --- /dev/null +++ b/_before.py @@ -0,0 +1,3 @@ + +def gs_init(m={}): + pass diff --git a/gosubl/_dbg.py b/gosubl/_dbg.py index 3ce52be7..267e75ca 100644 --- a/gosubl/_dbg.py +++ b/gosubl/_dbg.py @@ -1,30 +1,59 @@ +import inspect import sys import time +pf_enabled = False +print_enabled = False +gt_default = 0.020 + class pf(object): - def __init__(self, print='{name}: {dur}', name='', dot='', gt=0.020): + def __init__(self, *, format='slow op: {name}: {dur}', name='', dot='', gt=gt_default): self.start = time.time() self.end = 0 self.duration = 0 self.dur = '' - self.print = print + self.format = format self.gt = gt - try: - self.caller = sys._getframe(1).f_code.co_name - except ValueError: - self.caller = 'unknown' + self.caller = self._caller_name() self.name = name or self.caller self.dot = dot if self.dot: - self.name += '.' + self.dot + self.name = '%s..%s' % (self.name, self.dot) + + def _caller_name(self): + if not pf_enabled: + return '' + + try: + frame = sys._getframe(2) + except AttributeError: + return '' + + try: + klass = frame.f_locals['self'].__class__ + except KeyError: + klass = '' + + return '%s.%s' % (klass, frame.f_code.co_name) def __del__(self): self.end = time.time() self.duration = self.end - self.start self.dur = '%0.3fs' % self.duration + if not pf_enabled: + return + if self.duration <= self.gt: return - if self.print: - print(self.print.format(**self.__dict__)) + if self.format: + _println(self.format.format(**self.__dict__)) + +def _println(*a): + print('GoSublime_DBG:', *a) + +def println(*a): + if print_enabled: + _println(*a) + diff --git a/gosubl/about.py b/gosubl/about.py index fac929bb..5c31f605 100644 --- a/gosubl/about.py +++ b/gosubl/about.py @@ -1,8 +1,9 @@ import re import sublime -ANN = 'a18.07.17-1' -VERSION = 'r18.05.19-1' +TAG = '20.06.14-1' +ANN = 'a'+TAG +VERSION = 'r'+TAG VERSION_PAT = re.compile(r'\d{2}[.]\d{2}[.]\d{2}-\d+', re.IGNORECASE) DEFAULT_GO_VERSION = 'go?' GO_VERSION_OUTPUT_PAT = re.compile(r'go\s+version\s+(\S+(?:\s+[+]\w+|\s+\([^)]+)?)', re.IGNORECASE) diff --git a/gosubl/gs.py b/gosubl/gs.py index f2cc165d..a3947dee 100644 --- a/gosubl/gs.py +++ b/gosubl/gs.py @@ -70,7 +70,7 @@ "comp_lint_enabled": False, "comp_lint_commands": [], "gslint_timeout": 0, - "calltips": True, + "calltips": False, "autocomplete_snippets": False, "autocomplete_tests": False, "autocomplete_closures": False, @@ -84,7 +84,7 @@ "autosave": True, "build_command": [], "lint_filter": [], - "lint_enbled": True, + "lint_enbled": False, "linters": [], "9o_instance": "", "9o_color_scheme": "", @@ -154,8 +154,8 @@ 'constant.other.rune.go', ]) -VFN_ID_PAT = re.compile(r'^(?:gs\.)?view(?:#|://)(\d+)(.*?)$', re.IGNORECASE) -ROWCOL_PAT = re.compile(r'^[:]*(\d+)(?:[:](\d+))?[:]*$') +VFN_ID_PAT = re.compile(r'(?:gs\.)?view(?:#|@|://)(\d+)(.*?)$', re.IGNORECASE) +ROWCOL_PAT = re.compile(r'[:]+(\d+)(?:[:](\d+))?[:]*$') USER_DIR = os.path.expanduser('~') USER_DIR_PAT = re.compile(r'^%s/' % (re.escape(USER_DIR.replace('\\', '/').rstrip('/')))) @@ -503,12 +503,17 @@ def active_view(win=None, view=None): return win.active_view() +def active_wd(win=None, view=None): + v = active_view(win=win, view=view) + return basedir_or_cwd(v.file_name() if v else '') + def win_view(vfn=None, win=None): - if not win: - win = sublime.active_window() + wins = [win] + if win is None: + wins = sublime.windows() view = None - if win: + for win in wins: m = VFN_ID_PAT.match(vfn or '') if m: try: @@ -519,10 +524,13 @@ def win_view(vfn=None, win=None): break except Exception: gs.error_traceback(NAME) - elif not vfn or vfn == "": + + if view is None: + if not vfn or vfn == "": view = win.active_view() else: view = win.open_file(vfn) + return (win, view) def do_focus(fn, row, col, win, focus_pat, cb): @@ -531,18 +539,32 @@ def do_focus(fn, row, col, win, focus_pat, cb): notify(NAME, 'Cannot find file position %s:%s:%s' % (fn, row, col)) if cb: cb(False) - elif view.is_loading(): - focus(fn, row=row, col=col, win=win, focus_pat=focus_pat, cb=cb) - else: - win.focus_view(view) - if row <= 0 and col <= 0 and focus_pat: - r = view.find(focus_pat, 0) - if r: - row, col = view.rowcol(r.begin()) - view.run_command("gs_goto_row_col", { "row": row, "col": col }) + + return + + def run(): + r, c = row, col + + if r <= 0 and c <= 0 and focus_pat: + reg = view.find(focus_pat, 0) + if reg: + r, c = view.rowcol(reg.begin()) + + if r < 0: + r, c = rowcol(view) + + view.run_command("gs_goto_row_col", { "row": r, "col": c }) + if cb: cb(True) + win.focus_view(view) + if view.is_loading(): + sublime.set_timeout(run, 500) + else: + run() + + def focus(fn, row=0, col=0, win=None, timeout=100, focus_pat='^package ', cb=None): sublime.set_timeout(lambda: do_focus(fn, row, col, win, focus_pat, cb), timeout) @@ -737,6 +759,7 @@ def tm_path(name): 'doc': 'syntax/GoSublime-GsDoc.sublime-syntax', 'go': 'syntax/GoSublime-Go.sublime-syntax', 'gohtml': 'syntax/GoSublime-HTML.sublime-syntax', + 'hud': 'syntax/GoSublime-HUD.sublime-syntax', } return 'Packages/GoSublime/%s' % d[name] @@ -789,9 +812,13 @@ def home_path(*a): def json_decode(s, default): try: + if isinstance(s, bytes): + s = s.decode('utf-8') + res = json.loads(s) - if is_a(res, default): + if default is None or is_a(res, default): return (res, '') + return (res, 'Unexpected value type') except Exception as ex: return (default, 'Decode Error: %s' % ex) diff --git a/gosubl/margo.py b/gosubl/margo.py index 1d5e0d71..4ea27b9c 100644 --- a/gosubl/margo.py +++ b/gosubl/margo.py @@ -1,27 +1,68 @@ from . import _dbg from . import gs, gsq, sh from .margo_agent import MargoAgent -from .margo_common import OutputLogger, TokenCounter -from .margo_render import render, render_src -from .margo_state import State, actions, Config, _view_scope_lang +from .margo_common import OutputLogger, TokenCounter, Mutex +from .margo_render import render +from .margo_state import State, actions, client_actions, Config, _view_scope_lang, view_is_9o, MgView +from base64 import b64decode from collections import namedtuple import glob import os +import shlex import sublime import time +import webbrowser class MargoSingleton(object): def __init__(self): - self.package_dir = os.path.dirname(os.path.abspath(__file__)) + self._ready = False self.out = OutputLogger('margo') self.agent_tokens = TokenCounter('agent', format='{}#{:03d}', start=6) + self.run_tokens = TokenCounter('9o.run') self.agent = None - self.enabled_for_langs = [] + self.enabled_for_langs = ['*'] self.state = State() self.status = [] + self.output_handler = None + self._client_actions_handlers = { + client_actions.Activate: self._handle_act_activate, + client_actions.Restart: self._handle_act_restart, + client_actions.Shutdown: self._handle_act_shutdown, + client_actions.CmdOutput: self._handle_act_output, + client_actions.DisplayIssues: self._handle_DisplayIssues, + } + self.file_ids = [] + self._hud_state = {} + self._hud_state_lock = Mutex(name='margo.MargoSingleton._hud_state_lock') + self.hud_name = 'GoSublime/HUD' + self.hud_id = self.hud_name.replace('/','-').lower() + self._views = {} + self._view_lock = Mutex(name='margo.MargoSingleton._view_lock') + self._gopath = '' + + def _sync_settings(self): + old, new = self._gopath, sh.getenv('GOPATH') + + if not new or new == old: + return + + self._gopath = new + + ag = self.agent + if not ag or new == ag.gopath: + return + + self.out.println('Stopping agent. GOPATH changed: `%s` -> `%s`' % (ag.gopath, new)) + self.stop(ag=ag) def render(self, rs=None): + # ST has some locking issues due to its "thread-safe" API + # don't access things like sublime.active_view() directly + if rs: + for err in rs.state.errors: + self.out.println('Error: %s' % err) + self.state = rs.state cfg = rs.state.config @@ -30,21 +71,47 @@ def render(self, rs=None): if cfg.override_settings: gs._mg_override_settings = cfg.override_settings - render(view=gs.active_view(), state=self.state, status=self.status) + def _render(): + render( + mg=mg, + view=gs.active_view(), + state=self.state, + status=self.status, + ) + + if rs: + self._handle_client_actions(rs) + + if rs.agent and rs.agent is not self.agent: + rs.agent.stop() + + sublime.set_timeout(_render) - if rs: - if rs.agent is self.agent: - sublime.set_timeout_async(lambda: self._handle_client_actions(rs.state.client_actions), 0) - if rs.agent and rs.agent is not self.agent: - rs.agent.stop() + def _handle_act_activate(self, rs, act): + gs.focus(act.name or act.path, row=act.row, col=act.col, focus_pat='') - def _handle_client_actions(self, client_actions): - for a in client_actions: - if a.name == 'restart': - self.restart() - elif a.name == 'shutdown': - self.stop() + def _handle_act_restart(self, rs, act): + self.restart() + + def _handle_act_shutdown(self, rs, act): + self.stop() + + def _handle_act_output(self, rs, act): + h = self.output_handler + if h: + h(rs, act) + + def _handle_DisplayIssues(self, rs, act): + gs.active_view().run_command('margo_display_issues') + + def _handle_client_actions(self, rs): + for act in rs.state.client_actions: + f = self._client_actions_handlers.get(act.action_name) + if f: + f(rs, act) + else: + self.out.println('Unknown client-action: %s: %s' % (act.action_name, act)) def render_status(self, *a): self.status = list(a) @@ -64,12 +131,17 @@ def restart(self): self.agent = MargoAgent(self) self.agent.start() - def stop(self): - a, self.agent = self.agent, None - if a: - a.stop() + def stop(self, ag=None): + if not ag or ag is self.agent: + ag, self.agent = self.agent, None + + if ag: + ag.stop() def enabled(self, view): + if not self._ready: + return False + if '*' in self.enabled_for_langs: return True @@ -77,8 +149,7 @@ def enabled(self, view): return lang in self.enabled_for_langs def can_trigger_event(self, view, allow_9o=False): - if not self.enabled(view): - return False + _pf=_dbg.pf() if view is None: return False @@ -86,27 +157,190 @@ def can_trigger_event(self, view, allow_9o=False): if view.is_loading(): return False - vs = view.settings() - if allow_9o and vs.get('9o'): + if not self.enabled(view): + return False + + mgv = self.view(view.id(), view=view) + if allow_9o and mgv.is_9o: return True - if vs.get('is_widget'): + if not mgv.is_file: return False return True + def _gs_init(self): + self._sync_settings() + gs.sync_settings_callbacks.append(self._sync_settings) + + for w in sublime.windows(): + for v in w.views(): + if v is not None: + self.view(v.id(), view=v) + + mg._ready = True + mg.start() + + def _hud_create_panel(self, win): + view = win.create_output_panel(self.hud_name) + if win == sublime.active_window(): + win.focus_view(win.active_view()) + syntax = gs.tm_path('hud') + settings = view.settings() + if settings.get('syntax') == syntax: + return view + + view.set_syntax_file(syntax) + view.set_read_only(True) + view.set_name(self.hud_name) + opts = { + 'line_numbers': False, + 'gutter': False, + 'margin': 0, + 'highlight_line': False, + 'rulers': [], + 'fold_buttons': False, + 'scroll_past_end': False, + } + settings.erase('color_scheme') + for k, v in opts.items(): + settings.set(k, v) + + return view + + def is_hud_view(self, view): + if view is None: + return False + + return view.settings().get('syntax') == gs.tm_path('hud') + + def _hud_win_state(self, win): + default = (None, None) + if win is None: + return default + + return self._hud_state.get(win.id()) or default + + def hud_panel(self, win): + with self._hud_state_lock: + view, phantoms = self._hud_win_state(win) + wid = win.id() + m = self._hud_state + + if view is None: + view = self._hud_create_panel(win) + m[wid] = (view, phantoms) + + if phantoms is None: + phantoms = sublime.PhantomSet(view, self.hud_name) + m[wid] = (view, phantoms) + + if len(m) > 1: + wids = [w.id() for w in sublime.windows()] + for id in list(m.keys()): + if id not in wids: + del m[id] + + return (view, phantoms) + + def view(self, id, view=None): + with self._view_lock: + mgv = self._views.get(id) + + if view is not None: + if mgv is None: + mgv = MgView(mg=self, view=view) + self._views[mgv.id] = mgv + else: + mgv.sync(view=view) + + return mgv + + def _sync_view(self, event, view): + if event in ('pre_close', 'close'): + with self._view_lock: + self._views.pop(view.id(), None) + + return + + _pf=_dbg.pf(dot=event) + + file_ids = [] + for w in sublime.windows(): + for v in w.views(): + file_ids.append(v.id()) + + self.file_ids = file_ids + self.view(view.id(), view=view) + + with self._view_lock: + m = self._views + self._views = {k: m[k] for k in set(file_ids).intersection(set(m.keys()))} + def event(self, name, view, handler, args): - allow_9o = name in ( + if view is None: + return None + + _pf=_dbg.pf(dot=name) + + + win = view.window() + if self.is_hud_view(view): + view = gs.active_view(win=win) + win.focus_view(view) + + def handle_event(gt=0): + if gt > 0: + _pf.gt=gt + + self._sync_view(name, view) + + if not self.can_trigger_event(view): + return None + + try: + return handler(*args) + except Exception: + gs.error_traceback('mg.event:%s' % handler) + return None + + blocking = ( + 'pre_save', 'query_completions', ) - if not self.can_trigger_event(view, allow_9o=allow_9o): - return None - try: - return handler(*args) - except Exception: - gs.error_traceback('mg.event:%s' % handler) - return None + if name in blocking: + return handle_event(gt=0.100) + + sublime.set_timeout(handle_event) + + def _is_str(self, s): + return isinstance(s, str) + + def _is_act(self, m): + return isinstance(m, dict) and self._is_str(m.get('Name')) + + def _lst_of(self, l, f): + return isinstance(l, list) and l and len(list(filter(f, l))) == len(l) + + def navigate(self, href, *, view=None, win=None): + if href.startswith('https://') or href.startswith('http://'): + gsq.launch('mg.navigate', lambda: webbrowser.open_new_tab(href)) + return + + dataPfx = 'data:application/json;base64,' + data = b64decode(href[len(dataPfx):]) if href.startswith(dataPfx) else href + + view = gs.active_view(view=view, win=win) + x, err = gs.json_decode(data, None) + if self._is_act(x): + self.queue(actions=[x], view=view, delay=0.100) + elif self._lst_of(x, self._is_act): + self.queue(actions=x, view=view, delay=0.100) + elif self._lst_of(x, self._is_str): + view.window().run_command('gs9o_open', {'run': x, 'focus_view': False}) + else: + self.out.println('mg.navigate: Invalid href `%s`, expected `http(s)://` or data:json`{Name: action}|[command args...]`, error: %s' % (href, err)) def agent_starting(self, ag): if ag is not self.agent: @@ -132,52 +366,129 @@ def _send_start(self): if not self.agent: self.start() - def send(self, action={}, cb=None, view=None): + def queue(self, *, actions=[], view=None, delay=-1): + self._send_start() + self.agent.queue(actions=actions, view=view, delay=delay) + + def send(self, *, actions=[], cb=None, view=None): self._send_start() - return self.agent.send(action=action, cb=cb, view=view) + return self.agent.send(actions=actions, cb=cb, view=view) + + def on_new(self, view): + pass + + def on_pre_close(self, view): + pass def on_query_completions(self, view, prefix, locations): - action = actions.QueryCompletions.copy() - rs = self.send(view=view, action=action).wait(0.300) + _, lang = _view_scope_lang(view, 0) + if not lang: + return None + + act = actions.QueryCompletions + if lang == 'cmd-prompt': + act = self._cmd_completions_act(view, prefix, locations) + if not act: + return None + + view = gs.active_view(win=view.window()) + if view is None: + return None + + rq = self.send(view=view, actions=[act]) + rs = rq.wait(0.500) if not rs: self.out.println('aborting QueryCompletions. it did not respond in time') return None + if rs.error: + self.out.println('completion error: %s: %s' % (act, rs.error)) + return + + if rs.state.view.src: + self._fmt_rs( + view=view, + event='query_completions', + rq=rq, + rs=rs, + ) + cl = [c.entry() for c in rs.state.completions] opts = rs.state.config.auto_complete_opts return (cl, opts) if opts != 0 else cl - def on_hover(self, view, point, hover_zone): - if hover_zone != sublime.HOVER_TEXT: - return + def _cmd_completions_act(self, view, prefix, locations): + pos = locations[0] + line = view.line(pos) + src = view.substr(line) + if '#' not in src: + return None + + i = src.index('#') + while src[i] == ' ' or src[i] == '#': + i += 1 + + src = src[i:] + pos = pos - line.begin() - i + name = '' + args = shlex.split(src) + if args: + name = args[0] + args = args[1:] + + act = actions.QueryCmdCompletions.copy() + act['Data'] = { + 'Pos': pos, + 'Src': src, + 'Name': name, + 'Args': args, + } + + return act + + def on_hover(self, view, pt, zone): + act = actions.QueryTooltips.copy() + row, col = view.rowcol(pt) + act['Data'] = { + 'Row': row, + 'Col': col, + } + self.queue(view=view, actions=[act]) def on_activated(self, view): - self.send(view=view, action=actions.ViewActivated) + self.queue(view=view, actions=[actions.ViewActivated]) def on_modified(self, view): - self._send_start() - self.agent.view_modified(view) + self.queue(view=view, actions=[actions.ViewModified]) def on_selection_modified(self, view): - self._send_start() - self.agent.view_pos_changed(view) + self.queue(view=view, actions=[actions.ViewPosChanged]) def fmt(self, view): - return self._fmt_save(view=view, action=actions.ViewFmt, name='fmt', timeout=5.000) + return self._fmt_save(view=view, actions=[actions.ViewFmt], event='fmt', timeout=5.000) def on_pre_save(self, view): - return self._fmt_save(view=view, action=actions.ViewPreSave, name='pre-save', timeout=2.000) + return self._fmt_save(view=view, actions=[actions.ViewPreSave], event='pre_save', timeout=2.000) - def _fmt_save(self, *, view, action, name, timeout): - id_nm = '%d: %s' % (view.id(), view.file_name() or view.name()) - rq = self.send(view=view, action=action) + def _fmt_save(self, *, view, actions, event, timeout): + rq = self.send(view=view, actions=actions) rs = rq.wait(timeout) + self._fmt_rs( + view=view, + event=event, + rq=rq, + rs=rs, + ) + + def _fmt_rs(self, *, view, event, rq, rs): + id_nm = '%d: %s' % (view.id(), view.file_name() or view.name()) + if not rs: - self.out.println('%s timedout on view %s' % (name, id_nm)) + self.out.println('%s timedout on view %s' % (event, id_nm)) return if rs.error: - self.out.println('%s error in view %s: %s' % (name, id_nm, rs.error)) + self.out.println('%s error in view %s: %s' % (event, id_nm, rs.error)) return req = rq.props.get('View', {}) @@ -200,16 +511,13 @@ def _fmt_save(self, *, view, action, name, timeout): view.run_command('margo_render_src', {'src': res_src}) def on_post_save(self, view): - self.send(view=view, action=actions.ViewSaved) + self.queue(view=view, actions=[actions.ViewSaved]) def on_load(self, view): - self.send(view=view, action=actions.ViewLoaded) - - def on_close(self, view): - self.send(view=view, action=actions.ViewClosed) + self.on_activated(view) def example_extension_file(self): - return gs.dist_path('src/disposa.blue/margo/extension-example/extension-example.go') + return gs.dist_path('src/margo.sh/extension-example/extension-example.go') def extension_file(self, install=False): src_dir = gs.user_path('src', 'margo') @@ -224,8 +532,8 @@ def ext_fn(): try: gs.mkdirp(src_dir) - with open('%s/margo.go' % src_dir, 'x') as f: - s = open(self.example_extension_file(), 'r').read() + with open('%s/margo.go' % src_dir, 'xb') as f: + s = open(self.example_extension_file(), 'rb').read() f.write(s) except FileExistsError: pass @@ -238,7 +546,7 @@ def ext_fn(): mg = MargoSingleton() def gs_init(_): - mg.start() + sublime.set_timeout(mg._gs_init) def gs_fini(_): mg.stop() diff --git a/gosubl/margo_agent.py b/gosubl/margo_agent.py index 53e9e0a3..b4e01fb7 100644 --- a/gosubl/margo_agent.py +++ b/gosubl/margo_agent.py @@ -1,7 +1,8 @@ from . import _dbg from . import sh, gs, gsq -from .margo_common import TokenCounter, OutputLogger, Chan +from .margo_common import TokenCounter, OutputLogger, Chan, Mutex from .margo_state import State, make_props, actions +from datetime import datetime import os import sublime import subprocess @@ -9,17 +10,24 @@ import time ipc_codec = 'msgpack' - +ipc_silent_exceptions = ( + EOFError, + BrokenPipeError, + ValueError, +) if ipc_codec == 'msgpack': from .vendor import umsgpack - ipc_dec = umsgpack.load + ipc_loads = umsgpack.loads + ipc_dec = lambda fp: umsgpack.load(fp, allow_invalid_utf8=True) ipc_enc = umsgpack.dump - ipc_ignore_exceptions = (umsgpack.InsufficientDataException, BrokenPipeError) + ipc_silent_exceptions += ( + umsgpack.InsufficientDataException, + ) elif ipc_codec == 'cbor': from .vendor.cbor_py import cbor + ipc_loads = cbor.loads ipc_dec = cbor.load ipc_enc = cbor.dump - ipc_ignore_exceptions = (BrokenPipeError) else: raise Exception('impossibru') @@ -32,7 +40,7 @@ def __init__(self, mg): _, self.domain = mg.agent_tokens.next() self.cookies = TokenCounter('%s,request' % self.domain) self.proc = None - self.lock = threading.Lock() + self.lock = Mutex(name='margo.MargoAgent.lock') self.out = OutputLogger(self.domain, parent=mg.out) self.global_handlers = {} self.req_handlers = {} @@ -41,30 +49,39 @@ def __init__(self, mg): self.starting.set() self.started = threading.Event() self.stopped = threading.Event() + self._queue_ch = Chan(discard=1) self.ready = threading.Event() - gopaths = [ - os.path.join(sublime.packages_path(), 'User', 'margo'), - mg.package_dir, - ] - psep = os.pathsep - self._env = { - 'GOPATH': psep.join(gopaths), + gopaths = (gs.user_path(), gs.dist_path()) + psep = sh.psep + self.gopath = sh.getenv('GOPATH') + self.data_dir = gs.user_path('margo.data') + self._default_env = { + 'GOPATH': self.gopath, + 'MARGO_DATA_DIR': self.data_dir, + 'MARGO_AGENT_GO111MODULE': 'off', + 'MARGO_AGENT_GOPATH': psep.join(gopaths), 'PATH': psep.join([os.path.join(p, 'bin') for p in gopaths]) + psep + os.environ.get('PATH'), } + gs.mkdirp(self.data_dir) - self._mod_ev = threading.Event() - self._mod_view = None - self._pos_view = None + self._acts_lock = Mutex(name='margo.MargoAgent._acts_lock') + self._acts = [] def __del__(self): self.stop() + def _env(self, m): + e = self._default_env.copy() + e.update(m) + return e + def stop(self): if self.stopped.is_set(): return self.starting.clear() self.stopped.set() + self._queue_ch.close() self.req_chan.close() self._stop_proc() self._release_handlers() @@ -85,46 +102,51 @@ def run(self): self._start_proc() def _start_proc(self): + _dbg.pf(dot=self.domain) + self.mg.agent_starting(self) self.out.println('starting') - gs_gopath = sh.psep.join((gs.user_path(), gs.dist_path())) gs_gobin = gs.dist_path('bin') - install_cmd = ['go', 'install', '-v', 'disposa.blue/margo/cmd/margo'] + mg_exe = 'margo.sh' + install_cmd = ['go', 'install', '-v','-x', mg_exe] cmd = sh.Command(install_cmd) - cmd.env = { - 'GOPATH': gs_gopath, + cmd.env = self._env({ + 'GOPATH': self._default_env['MARGO_AGENT_GOPATH'], + 'GO111MODULE': self._default_env['MARGO_AGENT_GO111MODULE'], 'GOBIN': gs_gobin, - } + 'MARGO_AGENT_GOBIN': gs_gobin, + }) cr = cmd.run() for v in (cr.out, cr.err, cr.exc): if v: self.out.println('%s:\n%s' % (install_cmd, v)) mg_cmd = [ - sh.which('margo', m={'PATH': gs_gobin}) or 'margo', - 'sublime', '-codec', ipc_codec, + sh.which(mg_exe, m={'PATH': gs_gobin}) or mg_exe, + 'start', 'margo.sublime', '-codec', ipc_codec, ] self.out.println(mg_cmd) cmd = sh.Command(mg_cmd) - cmd.env = { - 'GOPATH': gs_gopath, + cmd.env = self._env({ 'PATH': gs_gobin, - } + }) pr = cmd.proc() if not pr.ok: self.stop() self.out.println('Cannot start margo: %s' % pr.exc) return + stderr = pr.p.stderr self.proc = pr.p gsq.launch(self.domain, self._handle_send) - gsq.launch(self.domain, self._handle_send_mod) + gsq.launch(self.domain, self._handle_queue) gsq.launch(self.domain, self._handle_recv) gsq.launch(self.domain, self._handle_log) self.started.set() self.starting.clear() self.proc.wait() + self._close_file(stderr) def _stop_proc(self): self.out.println('stopping') @@ -132,11 +154,19 @@ def _stop_proc(self): if not p: return - for f in (p.stdin, p.stdout, p.stderr): - try: - f.close() - except Exception as exc: - self.out.println(exc) + # stderr is closed after .wait() returns + for f in (p.stdin, p.stdout): + self._close_file(f) + + def _close_file(self, f): + if f is None: + return + + try: + f.close() + except Exception as exc: + self.out.println(exc) + gs.error_traceback(self.domain) def _handle_send_ipc(self, rq): with self.lock: @@ -145,8 +175,6 @@ def _handle_send_ipc(self, rq): try: ipc_enc(rq.data(), self.proc.stdin) exc = None - except ipc_ignore_exceptions as e: - exc = e except Exception as e: exc = e if not self.stopped.is_set(): @@ -158,8 +186,39 @@ def _handle_send_ipc(self, rq): rq.done(AgentRes(error='Exception: %s' % exc, rq=rq, agent=self)) - def send(self, action={}, cb=None, view=None): - rq = AgentReq(self, action, cb=cb, view=view) + def _queued_acts(self, view): + if view is None: + return [] + + with self._acts_lock: + q, self._acts = self._acts, [] + + acts = [] + for act, vid in q: + if vid == view.id(): + acts.append(act) + + return acts + + def queue(self, *, actions=[], view=None, delay=-1): + with self._acts_lock: + for act in actions: + p = (act, view.id()) + try: + self._acts.remove(p) + except ValueError: + pass + + self._acts.append(p) + + self._queue_ch.put(delay) + + def send(self, *, actions=[], cb=None, view=None): + view = gs.active_view(view=view) + if not isinstance(actions, list): + raise Exception('actions must be a list, not %s' % type(actions)) + acts = self._queued_acts(view) + actions + rq = AgentReq(self, acts, cb=cb, view=view) timeout = 0.200 if not self.started.wait(timeout): rq.done(AgentRes(error='margo has not started after %0.3fs' % (timeout), timedout=timeout, rq=rq, agent=self)) @@ -170,36 +229,16 @@ def send(self, action={}, cb=None, view=None): return rq - def view_modified(self, view): - self._mod_view = view - self._mod_ev.set() - - def view_pos_changed(self, view): - self._pos_view = view - self._mod_ev.set() - - def _send_mod(self): - mod_v, self._mod_view = self._mod_view, None - pos_v, self._pos_view = self._pos_view, None - if mod_v is None and pos_v is None: - return + def _send_acts(self): + view = gs.active_view() + acts = self._queued_acts(view) + if acts: + self.send(actions=acts, view=view).wait() - view = pos_v - action = actions.ViewPosChanged - if mod_v is not None: - action = actions.ViewModified - view = mod_v - - self.send(action=action, view=view).wait() - - def _handle_send_mod(self): - delay = 0.500 - while not self.stopped.is_set(): - self._mod_ev.wait(delay) - if self._mod_ev.is_set(): - self._mod_ev.clear() - time.sleep(delay * 1.5) - self._send_mod() + def _handle_queue(self): + for n in self._queue_ch: + time.sleep(n if n >= 0 else 0.600) + self._send_acts() def _handle_send(self): for rq in self.req_chan: @@ -247,10 +286,11 @@ def _handle_recv(self): v = ipc_dec(self.proc.stdout) or {} if v: self._handle_recv_ipc(v) - except ipc_ignore_exceptions: + except ipc_silent_exceptions: pass except Exception as e: self.out.println('ipc: recv: %s: %s' % (e, v)) + gs.error_traceback(self.domain) finally: self.stop() @@ -286,26 +326,23 @@ def __init__(self, v={}, error='', timedout=0, rq=None, agent=None): def set_rq(self, rq): if self.error and rq: - act = rq.action - if act and act.get('Name'): - self.error = 'action: %s, error: %s' % (act.get('Name'), self.error) - else: - self.error = 'error: %s' % self.error + self.error = 'actions: %s, error: %s' % (rq.actions_str, self.error) def get(self, k, default=None): return self.state.get(k, default) class AgentReq(object): - def __init__(self, agent, action, cb=None, view=None): + def __init__(self, agent, actions, cb=None, view=None): self.start_time = time.time() + self.actions = actions + self.actions_str = ' ~> '.join(a['Name'] for a in actions) _, cookie = agent.cookies.next() - self.cookie = 'action:%s(%s)' % (action['Name'], cookie) + self.cookie = 'actions(%s),%s' % (self.actions_str, cookie) self.domain = self.cookie - self.action = action self.cb = cb self.props = make_props(view=view) self.rs = DEFAULT_RESPONSE - self.lock = threading.Lock() + self.lock = Mutex(name='margo.AgentReq.lock') self.ev = threading.Event() self.view = view @@ -333,7 +370,8 @@ def data(self): return { 'Cookie': self.cookie, 'Props': self.props, - 'Action': self.action, + 'Actions': self.actions, + 'Sent': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f'), } DEFAULT_RESPONSE = AgentRes(error='default agent response') diff --git a/gosubl/margo_common.py b/gosubl/margo_common.py index 3f3a07fa..92dd5891 100644 --- a/gosubl/margo_common.py +++ b/gosubl/margo_common.py @@ -5,6 +5,23 @@ import sublime import time +class Mutex(object): + def __init__(self, *, name=''): + self.name = name + self.lck = threading.Lock() + + def __enter__(self): + self.lock() + + def __exit__(self, type, value, traceback): + self.unlock() + + def lock(self): + self.lck.acquire(True) + + def unlock(self): + self.lck.release() + class OutputLogger(object): def __init__(self, domain, parent=None): self.domain = domain @@ -38,10 +55,10 @@ def next(self): return self.n, self.format.format(self.name, self.n) class Chan(object): - def __init__(self, zero=None): - self.lock = threading.Lock() + def __init__(self, *, zero=None, discard=None): + self.lock = Mutex(name='margo.Chan') self.ev = threading.Event() - self.dq = deque() + self.dq = deque([], maxlen=discard) self.closed = False self.zero = zero diff --git a/gosubl/margo_render.py b/gosubl/margo_render.py index 5918d824..09362e5e 100644 --- a/gosubl/margo_render.py +++ b/gosubl/margo_render.py @@ -1,20 +1,24 @@ +from . import about from . import _dbg from . import gs from . import gspatch -from .margo_state import view_name, view_path +from .margo_state import ViewPathName import sublime + STATUS_KEY = '#mg.Status' -STATUS_PFX = '• ' -STATUS_SFX = ' •' -STATUS_SEP = ' •• ' +STATUS_PFX = ' ' +STATUS_SFX = ' ' +STATUS_SEP = ' ' -def render(view, state, status=[]): - sublime.set_timeout_async(lambda: _render(view, state, status), 0) +def render(*, mg, view, state, status): + def cb(): + _render_tooltips(view, state.tooltips) + _render_status(view, status + state.status) + _render_issues(view, state.issues) + _render_hud(mg=mg, state=state, view=view) -def _render(view, state, status): - _render_status(view, status + state.status) - _render_issues(view, state.issues) + sublime.set_timeout(cb) def _render_status(view, status): if status: @@ -46,28 +50,34 @@ def __init__(self, *, key, scope, icon, flags): issue_key_pfx = '#mg.Issue.' issue_cfg_error = IssueCfg( key = issue_key_pfx + 'error', - scope = 'keyword sublimelinter.mark.error region.redish', + scope = 'region.redish', icon = 'Packages/GoSublime/images/issue.png', flags = sublime.DRAW_SQUIGGLY_UNDERLINE | sublime.DRAW_NO_OUTLINE | sublime.DRAW_NO_FILL, ) issue_cfg_warning = IssueCfg( key = issue_key_pfx + 'warning', - scope = 'entity sublimelinter.mark.warning region.orangish', + scope = 'region.orangish', + icon = issue_cfg_error.icon, + flags = issue_cfg_error.flags, +) +issue_cfg_notice = IssueCfg( + key = issue_key_pfx + 'notice', + scope = 'region.greenish', icon = issue_cfg_error.icon, flags = issue_cfg_error.flags, ) -issue_cfg_default = issue_cfg_warning +issue_cfg_default = issue_cfg_error issue_cfgs = { 'error': issue_cfg_error, 'warning': issue_cfg_warning, + 'notice': issue_cfg_notice, } def _render_issues(view, issues): regions = {cfg.key: (cfg, []) for cfg in issue_cfgs.values()} - path = view_path(view) - name = view_name(view) + vp = ViewPathName(view) for isu in issues: - if path == isu.path or name == isu.name: + if isu.match(vp): cfg = issue_cfgs.get(isu.tag) or issue_cfg_default regions[cfg.key][1].append(_render_issue(view, isu)) @@ -96,3 +106,124 @@ def _render_issue(view, isu): return sublime.Region(sp, ep) +def _render_tooltips(view, tooltips): + if not tooltips: + return + + def ren(t): + return '''

%s

''' % (t.content) + + content = ''' + + + %s + + ''' % ''.join(ren(t) for t in tooltips) + + flags = sublime.COOPERATE_WITH_AUTO_COMPLETE | sublime.HIDE_ON_MOUSE_MOVE_AWAY + location = -1 + max_width = 640 + max_height = 480 + + def on_navigate(href): + pass + + def on_hide(): + pass + + view.show_popup( + content, + flags=flags, + location=location, + max_width=max_width, + max_height=max_height, + on_navigate=on_navigate, + on_hide=on_hide + ) + +def _render_hud(*, mg, state, view): + html = ''' + + + +
+ GoSublime/HUD + · + margo.sh/cl/%s +
+ +
+ %s +
+ + + ''' % ( + mg.hud_id, + about.VERSION, + about.VERSION, + ''.join(state.hud.articles), + ) + def ren(win): + v, phantoms = mg.hud_panel(win) + phantom = sublime.Phantom( + sublime.Region(v.size()), + html, + sublime.LAYOUT_INLINE, + lambda href: mg.navigate(href, view=view), + ) + vp = v.viewport_position() + phantoms.update([phantom]) + v.set_viewport_position(vp) + + for w in sublime.windows(): + ren(w) diff --git a/gosubl/margo_state.py b/gosubl/margo_state.py index 144f3b40..e225c5b2 100644 --- a/gosubl/margo_state.py +++ b/gosubl/margo_state.py @@ -1,14 +1,18 @@ from . import _dbg -from . import gs, sh +from . import gs, sh, about from .margo_common import NS +from os.path import basename, splitext import os import re import sublime actions = NS(**{k: {'Name': k} for k in ( 'QueryCompletions', + 'QueryCmdCompletions', 'QueryTooltips', 'QueryIssues', + 'QueryUserCmds', + 'QueryTestCmds', 'ViewActivated', 'ViewModified', 'ViewPosChanged', @@ -16,13 +20,56 @@ 'ViewPreSave', 'ViewSaved', 'ViewLoaded', - 'ViewClosed', + 'RunCmd', )}) +client_actions = NS(**{k: k for k in ( + 'Activate', + 'Restart', + 'Shutdown', + 'CmdOutput', + 'DisplayIssues', +)}) + +class MgView(sublime.View): + def __init__(self, *, mg, view): + self.mg = mg + self.is_9o = False + self.is_file = False + self.is_widget = False + self.sync(view=view) + + def sync(self, *, view): + if view is None: + return + + _pf=_dbg.pf(dot=self.id) + self.id = view.id() + self.view = view + self.name = view_name(view) + self.is_file = self.id in self.mg.file_ids + self.is_widget = not self.is_file + + def __eq__(self, v): + return self.view == v + + def __hash__(self): + return self.id + + def __repr__(self): + return repr(vars(self)) + + def name(self): + return view_name(self.view) + class Config(object): def __init__(self, m): + efl = m.get('EnabledForLangs') + if m and (not isinstance(efl, list) or len(efl) == 0): + print('MARGO BUG: EnabledForLangs is invalid.\nIt must be a non-empty list, not `%s: %s`\nconfig data: %s' % (type(efl), efl, m)) + self.override_settings = m.get('OverrideSettings') or {} - self.enabled_for_langs = m.get('EnabledForLangs') or [] + self.enabled_for_langs = efl or ['*'] self.inhibit_explicit_completions = m.get('InhibitExplicitCompletions') is True self.inhibit_word_completions = m.get('InhibitWordCompletions') is True self.auto_complete_opts = 0 @@ -37,19 +84,61 @@ def __repr__(self): class State(object): def __init__(self, v={}): self.config = Config(v.get('Config') or {}) + self.errors = v.get('Errors') or [] self.status = v.get('Status') or [] self.view = ResView(v=v.get('View') or {}) - self.client_actions = [ClientAction(v=a) for a in (v.get('ClientActions') or [])] self.completions = [Completion(c) for c in (v.get('Completions') or [])] self.tooltips = [Tooltip(t) for t in (v.get('Tooltips') or [])] self.issues = [Issue(l) for l in (v.get('Issues') or [])] + self.user_cmds = [UserCmd(c) for c in (v.get('UserCmds') or [])] + self.hud = HUD(v=v.get('HUD') or {}) + + self.client_actions = [] + for ca in (v.get('ClientActions') or []): + CA = client_action_creators.get(ca.get('Name') or '') or ClientAction + self.client_actions.append(CA(v=ca)) def __repr__(self): return repr(self.__dict__) class ClientAction(object): def __init__(self, v={}): - self.name = v.get('Name') or '' + self.action_name = v.get('Name') or '' + self.action_data = v.get('Data') or {} + + def __repr__(self): + return repr(vars(self)) + +class ClientAction_Output(ClientAction): + def __init__(self, v): + super().__init__(v=v) + ad = self.action_data + + self.fd = ad.get('Fd') or '' + self.output = ad.get('Output') or '' + self.close = ad.get('Close') or False + self.fd = ad.get('Fd') or '' + + def __repr__(self): + return repr(vars(self)) + +class ClientAction_Activate(ClientAction): + def __init__(self, v): + super().__init__(v=v) + ad = self.action_data + + self.path = ad.get('Path') or '' + self.name = ad.get('Name') or '' + self.row = ad.get('Row') or 0 + self.col = ad.get('Col') or 0 + + def __repr__(self): + return repr(vars(self)) + +client_action_creators = { + client_actions.CmdOutput: ClientAction_Output, + client_actions.Activate: ClientAction_Activate, +} class Completion(object): def __init__(self, v): @@ -69,15 +158,41 @@ def __repr__(self): class Tooltip(object): def __init__(self, v): - pass + self.content = v.get('Content') or '' def __repr__(self): return repr(self.__dict__) -class Issue(object): +class PathName(object): + def __init__(self, *, path, name): + self.path = path or '' + self.name = name or '' + + def match(self, p): + if self.path and self.path == p.path: + return True + + if self.name and self.name == p.name: + return True + + return False + + def __repr__(self): + return repr(vars(self)) + +class ViewPathName(PathName): + def __init__(self, view): + super().__init__( + path = view_path(view), + name = view_name(view), + ) + +class Issue(PathName): def __init__(self, v): - self.path = v.get('Path') or '' - self.name = v.get('Name') or '' + super().__init__( + path = v.get('Path') or '', + name = v.get('Name') or '', + ) self.hash = v.get('Hash') or '' self.row = v.get('Row') or 0 self.col = v.get('Col') or 0 @@ -98,6 +213,12 @@ def relpath(self, dir): return os.path.relpath(self.path, dir) + def basename(self): + if not self.path: + return self.name + + return os.path.basename(self.path) + class ResView(object): def __init__(self, v={}): self.name = v.get('Name') or '' @@ -105,9 +226,22 @@ def __init__(self, v={}): if isinstance(self.src, bytes): self.src = self.src.decode('utf-8') +class UserCmd(object): + def __init__(self, v={}): + self.title = v.get('Title') or '' + self.desc = v.get('Desc') or '' + self.name = v.get('Name') or '' + self.args = v.get('Args') or [] + self.dir = v.get('Dir') or '' + self.prompts = v.get('Prompts') or [] + +class HUD(object): + def __init__(self, v={}): + self.articles = v.get('Articles') or [] + # in testing, we should be able to push 50MiB+ files constantly without noticing a performance problem # but keep this number low (realistic source files sizes) at least until we optimize things -MAX_VIEW_SIZE = 512 << 10 +MAX_VIEW_SIZE = 8 << 20 # TODO: only send the content when it actually changes # TODO: do chunked copying i.e. copy e.g. 1MiB at a time @@ -115,11 +249,11 @@ def __init__(self, v={}): # if we attempt to copy large files because it has to convert into utf* # which could use up to x4 to convert into the string it gives us # and then we have to re-encode that into bytes to send it -def make_props(view=None): +def make_props(view=None, wd=''): props = { 'Editor': _editor_props(view), 'Env': sh.env(), - 'View': _view_props(view), + 'View': _view_props(view, wd=wd), } return props @@ -132,55 +266,53 @@ def _editor_props(view): return { 'Name': 'sublime', 'Version': sublime.version(), + 'Client': { + 'Name': 'gosublime', + 'Tag': about.TAG, + }, 'Settings': sett, } -def _view_props(view): - view = gs.active_view(view=view) +def view_is_9o(view): + return view is not None and view.settings().get('9o') + +def _view_props(view, wd=''): + was_9o = view_is_9o(view) + if was_9o: + view = gs.active_view() + else: + view = gs.active_view(view=view) + if view is None: return {} pos = gs.sel(view).begin() - row, col = view.rowcol(pos) scope, lang, fn, props = _view_header(view, pos) - wd = gs.basedir_or_cwd(fn) - - if lang == '9o': - if 'prompt.9o' in scope: - r = view.extract_scope(pos) - pos -= r.begin() - s = view.substr(r) - src = s.lstrip().lstrip('#').lstrip() - pos -= len(s) - len(src) - src = src.rstrip() - else: - pos = 0 - src = '' - - wd = view.settings().get('9o.wd') or wd - props['Path'] = '_.9o' - else: - src = _view_src(view) + wd = wd or gs.getwd() or gs.basedir_or_cwd(fn) + src = _view_src(view, lang) props.update({ 'Wd': wd, 'Pos': pos, - 'Row': row, - 'Col': col, 'Dirty': view.is_dirty(), 'Src': src, }) return props +_sanitize_view_name_pat = re.compile(r'[^-~,.@\w]') + def view_name(view, ext='', lang=''): if view is None: - return '' + return '_._' + nm = basename(view.file_name() or view.name() or '_') + nm, nm_ext = splitext(nm) if not ext: - ext = _view_ext(view, lang=lang) - - return 'view#' + _view_id(view) + ext + ext = _view_ext(view, lang=lang) or nm_ext or '._' + nm = 'view@%s,%s%s' % (_view_id(view), nm, ext) + nm = _sanitize_view_name_pat.sub('', nm) + return nm def view_path(view): if view is None: @@ -204,7 +336,6 @@ def _view_header(view, pos): return scope, lang, path, { 'Path': path, 'Name': view_name(view, ext=ext, lang=lang), - 'Ext': ext, 'Hash': _view_hash(view), 'Lang': lang, 'Scope': scope, @@ -222,23 +353,54 @@ def _view_hash(view): return 'id=%s,change=%d' % (_view_id(view), view.change_count()) -_scope_lang_pat = re.compile(r'source[.]([^\s.]+)') + +_lang_by_basename = { + 'go.mod': 'go.mod', + 'go.sum': 'go.sum', +} +_scope_lang_pat = re.compile(r'(?:source\.\w+|source|text)[.]([^\s.]+)') def _view_scope_lang(view, pos): if view is None: return ('', '') + _pf=_dbg.pf() scope = view.scope_name(pos).strip().lower() + + if view_is_9o(view): + return (scope, 'cmd-prompt') + + nm = basename(view_path(view)) + lb = _lang_by_basename.get(nm) + if lb: + return (scope, lb) + l = _scope_lang_pat.findall(scope) - lang = l[-1] if l else scope.split('.')[-1] + if not l: + return (scope, '') + + blacklist = ( + 'plain', + 'find-in-files', + ) + lang = l[-1] + if lang in blacklist: + return (scope, '') + return (scope, lang) -def _view_src(view): +def _view_src(view, lang): if view is None: return '' + if not lang: + return '' + if not view.is_dirty(): return '' + if view.is_loading(): + return '' + if view.size() > MAX_VIEW_SIZE: return '' diff --git a/gosubl/margo_sublime.py b/gosubl/margo_sublime.py index 99fdcab4..44c762c1 100644 --- a/gosubl/margo_sublime.py +++ b/gosubl/margo_sublime.py @@ -2,7 +2,7 @@ from . import gs from .margo import mg from .margo_render import render_src -from .margo_state import actions, view_path, view_name +from .margo_state import actions, ViewPathName import os import sublime import sublime_plugin @@ -11,35 +11,100 @@ class MargoEvents(sublime_plugin.EventListener): def on_query_completions(self, view, prefix, locations): return mg.event('query_completions', view, mg.on_query_completions, [view, prefix, locations]) - def on_hover(self, view, point, hover_zone): - return mg.event('hover', view, mg.on_hover, [view, point, hover_zone]) - - def on_activated_async(self, view): + def on_activated(self, view): return mg.event('activated', view, mg.on_activated, [view]) - def on_modified_async(self, view): + def on_modified(self, view): return mg.event('modified', view, mg.on_modified, [view]) - def on_selection_modified_async(self, view): + def on_selection_modified(self, view): return mg.event('selection_modified', view, mg.on_selection_modified, [view]) def on_pre_save(self, view): return mg.event('pre_save', view, mg.on_pre_save, [view]) - def on_post_save_async(self, view): + def on_post_save(self, view): return mg.event('post_save', view, mg.on_post_save, [view]) - def on_load_async(self, view): + def on_load(self, view): return mg.event('load', view, mg.on_load, [view]) - def on_close(self, view): - return mg.event('close', view, mg.on_close, [view]) + def on_new(self, view): + return mg.event('new', view, mg.on_new, [view]) + + def on_pre_close(self, view): + return mg.event('pre_close', view, mg.on_pre_close, [view]) + + def on_hover(self, view, point, hover_zone): + return mg.event('hover', view, mg.on_hover, [view, point, hover_zone]) class MargoRenderSrcCommand(sublime_plugin.TextCommand): def run(self, edit, src): render_src(self.view, edit, src) -class MargoIssuesCommand(sublime_plugin.TextCommand): +class MargoUserCmdsCommand(sublime_plugin.TextCommand): + def enabled(self): + return mg.enabled(self.view) + + def run(self, edit, action='QueryUserCmds'): + act = getattr(actions, action) + mg.send(view=self.view, actions=[act], cb=lambda rs: self._cb(rs=rs, action=action)) + + def _on_done(self, *, win, cmd, prompts): + if len(prompts) >= len(cmd.prompts): + self._on_done_call(win=win, cmd=cmd, prompts=prompts) + return + + def on_done(s): + prompts.append(s) + self._on_done(win=win, cmd=cmd, prompts=prompts) + + win.show_input_panel('%d/%d %s' % ( + len(prompts) + 1, + len(cmd.prompts), + cmd.prompts[len(prompts)-1], + ), '', on_done, None, None) + + def _on_done_call(self, *, win, cmd, prompts): + win.run_command('gs9o_win_open', { + 'run': [cmd.name] + cmd.args, + 'action_data': { + 'Prompts': prompts, + 'Dir': cmd.dir, + }, + 'save_hist': False, + 'focus_view': False, + 'show_view': True, + }) + + def _cb(self, *, rs, action): + win = self.view.window() or sublime.active_window() + selected = 0 + flags = sublime.MONOSPACE_FONT + items = [] + cmds = rs.state.user_cmds + + for c in cmds: + desc = c.desc + if not desc: + desc = '`%s`' % ' '.join([c.name] + c.args) + if c.dir: + sim = gs.simple_fn(c.dir) + rel = os.path.relpath(c.dir, gs.active_wd(win=win)) + if rel != '.': + desc += ' [ %s ]' % (rel if len(rel) < len(sim) else sim) + items.append([c.title, desc]) + + def on_done(i): + if i >= 0 and i < len(cmds): + self._on_done(win=win, cmd=cmds[i], prompts=[]) + + def on_highlight(i): + pass + + win.show_quick_panel(items or ['%s returned no results' % action], on_done, flags, selected, on_highlight) + +class margo_display_issues(sublime_plugin.TextCommand): def run(self, edit, **action): if mg.enabled(self.view): self._run() @@ -49,20 +114,19 @@ def run(self, edit, **action): }) def _run(self): - mg.send(view=self.view, action=actions.QueryIssues, cb=self._cb) + mg.send(view=self.view, actions=[actions.QueryIssues], cb=self._cb) def _cb(self, rs): show_issues(self.view, rs.state.issues) +class margo_issues(margo_display_issues): + pass + def issues_to_items(view, issues): - path = view_path(view) - dir = os.path.dirname(path) - name = view_name(view) + vp = ViewPathName(view) + dir = os.path.dirname(vp.path) index = [] - def in_view(isu): - return isu.path == path or isu.name == name or (not isu.path and not isu.name) - for isu in issues: if isu.message: index.append(isu) @@ -71,7 +135,7 @@ def in_view(isu): return ([], [], -1) def sort_key(isu): - if in_view(isu): + if vp.match(isu): return (-1, '', isu.row) return (1, isu.relpath(dir), isu.row) @@ -82,15 +146,28 @@ def sort_key(isu): items = [] selected = [] for idx, isu in enumerate(index): - if in_view(isu): - title = 'Line %d' % (isu.row + 1) + if vp.match(isu): + title = '%s:%d' % (isu.basename(), isu.row + 1) selected.append((abs(isu.row - row), idx)) else: title = '%s:%d' % (isu.relpath(dir) or isu.name, isu.row + 1) selected.append((999999999, -1)) - message = ' %s%s' % (isu.message, ' [' + isu.label + ']' if isu.label else '') - items.append([title, message]) + rows = [title] + rows.extend(s.strip() for s in isu.message.split('\n')) + rows.append(' '.join( + '[%s]' % s for s in filter(bool, (isu.tag, isu.label)) + )) + + # hack: ST sometimes decide to truncate the message because it's longer + # than the top row... and we don't want the message up there + rows[0] = rows[0].ljust(max(len(s) for s in rows)) + items.append(rows) + + # hack: if the items don't have the same length, ST throws an exception + n = max(len(l) for l in items) + for l in items: + l += [''] * (n - len(l)) return (items, index, min(selected)[1]) @@ -100,8 +177,12 @@ def show_issues(view, issues): items, index, selected = issues_to_items(view, issues) def on_done(i): - if i < 0 or i >= len(items): - fn = view_path(view) or view_name(view) + if not index or i >= len(index): + return + + if i < 0: + vp = ViewPathName(view) + fn = vp.path or vp.name gs.focus(fn, row=orig_row, col=orig_col, win=view.window()) return @@ -128,5 +209,10 @@ class MargoOpenExtensionCommand(sublime_plugin.WindowCommand): def run(self): fn = mg.extension_file(True) if fn: - gs.focus(fn, focus_pat='func Margo') + gs.focus(fn, row=-1, focus_pat='') + +class margo_show_hud(sublime_plugin.WindowCommand): + def run(self): + self.window.run_command('show_panel', {'panel': 'output.%s' % mg.hud_name}) + self.window.focus_view(self.window.active_view()) diff --git a/gosubl/mg9.py b/gosubl/mg9.py index a5d6b510..77f0bbab 100644 --- a/gosubl/mg9.py +++ b/gosubl/mg9.py @@ -185,6 +185,7 @@ def install(aso_install_vesion, force_install, _reinstall=False): 'CGO_ENABLED': '0', 'GOBIN': '', 'GOPATH': install_gopath(), + 'GO111MODULE': 'off', } ev.debug('%s.build' % DOMAIN, { diff --git a/gosubl/sh-bootstrap.go b/gosubl/sh-bootstrap.go index a887f6dc..98a7f0b5 100644 --- a/gosubl/sh-bootstrap.go +++ b/gosubl/sh-bootstrap.go @@ -16,16 +16,15 @@ func main() { m := reVer.FindStringSubmatch(rawVer) ver := reClean.ReplaceAllString(m[1], "..") env := map[string]string{ - "GOROOT": build.Default.GOROOT, - "GOPATH": build.Default.GOPATH, - "GOBIN": os.Getenv("GOBIN"), - "PATH": os.Getenv("PATH"), - "CGO_ENABLED": os.Getenv("CGO_ENABLED"), + "GOROOT": build.Default.GOROOT, + "GOPATH": build.Default.GOPATH, + "PATH": os.Getenv("PATH"), } + varPat := regexp.MustCompile(`^((?:MARGO|GO|CGO)\w+)=(.+)$`) for _, s := range os.Environ() { - l := strings.SplitN(s, "=", 2) - if len(l) == 2 && strings.HasPrefix(l[0], "MARGO_") { - env[l[0]] = l[1] + m := varPat.FindStringSubmatch(s) + if len(m) == 3 { + env[m[1]] = m[2] } } diff --git a/gosubl/sh.py b/gosubl/sh.py index c9700a6c..bcf423d7 100644 --- a/gosubl/sh.py +++ b/gosubl/sh.py @@ -48,11 +48,6 @@ def proc(self): else: input = None - try: - setsid = os.setsid - except Exception: - setsid = None - out = '' err = '' exc = None @@ -82,7 +77,7 @@ def proc(self): shell=False, env=nv, cwd=wd, - preexec_fn=setsid, + start_new_session=True, bufsize=0 ) except Exception as e: @@ -206,12 +201,13 @@ def gs_init(_={}): bs_fn = gs.file_path('gosubl/sh-bootstrap.go') bs_exe = gs.file_path('bin/gosubl-sh-bootstrap.exe') - def run(cmd_str): + def run(cmd_str, *, env={}): cmd = ShellCommand(cmd_str) cmd.wd = root_dir + cmd.env = env return cmd.run() - cr = run('go build -o %s %s' % (bs_exe, bs_fn)) + cr = run('go build -o %s %s' % (bs_exe, bs_fn), env={'GO111MODULE': 'off'}) if cr.exc or cr.err: _print('error building %s: %s' % (bs_fn, cr.exc or cr.err)) @@ -321,6 +317,8 @@ def env(m={}): m = m.copy() del m['PATH'] + add_path.append(gs.dist_path('bin')) + add_path.append(gs.user_path('bin')) add_path.append(bin_dir()) e = st_environ.copy() diff --git a/gosubl/vendor/umsgpack.py b/gosubl/vendor/umsgpack.py index cd7a2037..422ec381 100644 --- a/gosubl/vendor/umsgpack.py +++ b/gosubl/vendor/umsgpack.py @@ -1,4 +1,4 @@ -# u-msgpack-python v2.4.1 - v at sergeev.io +# u-msgpack-python v2.5.0 - v at sergeev.io # https://github.com/vsergeev/u-msgpack-python # # u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -31,7 +31,7 @@ # THE SOFTWARE. # """ -u-msgpack-python v2.4.1 - v at sergeev.io +u-msgpack-python v2.5.0 - v at sergeev.io https://github.com/vsergeev/u-msgpack-python u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -45,13 +45,14 @@ """ import struct import collections +import datetime import sys import io -__version__ = "2.4.1" +__version__ = "2.5.0" "Module version string" -version = (2, 4, 1) +version = (2, 5, 0) "Module version tuple" @@ -71,13 +72,9 @@ def __init__(self, type, data): Construct a new Ext object. Args: - type: application-defined type integer from 0 to 127 + type: application-defined type integer data: application-defined data byte array - Raises: - TypeError: - Specified ext type is outside of 0 to 127 range. - Example: >>> foo = umsgpack.Ext(0x05, b"\x01\x02\x03") >>> umsgpack.packb({u"special stuff": foo, u"awesome": True}) @@ -87,9 +84,9 @@ def __init__(self, type, data): Ext Object (Type: 0x05, Data: 01 02 03) >>> """ - # Application ext type should be 0 <= type <= 127 - if not isinstance(type, int) or not (type >= 0 and type <= 127): - raise TypeError("ext type out of range") + # Check type is type int + if not isinstance(type, int): + raise TypeError("ext type is not type integer") # Check data is type bytes elif sys.version_info[0] == 3 and not isinstance(data, bytes): raise TypeError("ext data is not type \'bytes\'") @@ -168,6 +165,11 @@ class InvalidStringException(UnpackException): pass +class UnsupportedTimestampException(UnpackException): + "Unsupported timestamp format encountered during unpacking." + pass + + class ReservedCodeException(UnpackException): "Reserved code encountered during unpacking." pass @@ -341,6 +343,29 @@ def _pack_ext(obj, fp, options): raise UnsupportedTypeException("huge ext data") +def _pack_ext_timestamp(obj, fp, options): + delta = obj - _epoch + seconds = delta.seconds + delta.days * 86400 + microseconds = delta.microseconds + + if microseconds == 0 and 0 <= seconds <= 2**32 - 1: + # 32-bit timestamp + fp.write(b"\xd6\xff" + + struct.pack(">I", seconds)) + elif 0 <= seconds <= 2**34 - 1: + # 64-bit timestamp + value = ((microseconds * 1000) << 34) | seconds + fp.write(b"\xd7\xff" + + struct.pack(">Q", value)) + elif -2**63 <= abs(seconds) <= 2**63 - 1: + # 96-bit timestamp + fp.write(b"\xc7\x0c\xff" + + struct.pack(">I", microseconds * 1000) + + struct.pack(">q", seconds)) + else: + raise UnsupportedTypeException("huge timestamp") + + def _pack_array(obj, fp, options): if len(obj) <= 15: fp.write(struct.pack("B", 0x90 | len(obj))) @@ -428,6 +453,8 @@ def _pack2(obj, fp, **options): _pack_array(obj, fp, options) elif isinstance(obj, dict): _pack_map(obj, fp, options) + elif isinstance(obj, datetime.datetime): + _pack_ext_timestamp(obj, fp, options) elif isinstance(obj, Ext): _pack_ext(obj, fp, options) elif ext_handlers: @@ -498,6 +525,8 @@ def _pack3(obj, fp, **options): _pack_array(obj, fp, options) elif isinstance(obj, dict): _pack_map(obj, fp, options) + elif isinstance(obj, datetime.datetime): + _pack_ext_timestamp(obj, fp, options) elif isinstance(obj, Ext): _pack_ext(obj, fp, options) elif ext_handlers: @@ -584,10 +613,26 @@ def _packb3(obj, **options): def _read_except(fp, n): - data = fp.read(n) - if len(data) < n: - raise InsufficientDataException() - return data + # when reading from files, networks, etc. there's no guarantee that a read(n) + # will return n bytes, so we must keep reading until we get n bytes + data = None + for _ in range(1000): + s = fp.read(n) + n -= len(s) + + if data is None: + data = s + else: + data += s + + if n <= 0: + return data + + if len(s) == 0: + # AFAIK, Python won't return 0 bytes unless we reached EOF + raise InsufficientDataException() + + raise InsufficientDataException() def _unpack_integer(code, fp, options): @@ -703,16 +748,46 @@ def _unpack_ext(code, fp, options): else: raise Exception("logic error, not ext: 0x%02x" % ord(code)) - ext = Ext(ord(_read_except(fp, 1)), _read_except(fp, length)) + ext_type = struct.unpack("b", _read_except(fp, 1))[0] + ext_data = _read_except(fp, length) + + # Create extension object + ext = Ext(ext_type, ext_data) # Unpack with ext handler, if we have one ext_handlers = options.get("ext_handlers") if ext_handlers and ext.type in ext_handlers: - ext = ext_handlers[ext.type](ext) + return ext_handlers[ext.type](ext) + + # Timestamp extension + if ext.type == -1: + return _unpack_ext_timestamp(ext, options) return ext +def _unpack_ext_timestamp(ext, options): + if len(ext.data) == 4: + # 32-bit timestamp + seconds = struct.unpack(">I", ext.data)[0] + microseconds = 0 + elif len(ext.data) == 8: + # 64-bit timestamp + value = struct.unpack(">Q", ext.data)[0] + seconds = value & 0x3ffffffff + microseconds = (value >> 34) // 1000 + elif len(ext.data) == 12: + # 96-bit timestamp + seconds = struct.unpack(">q", ext.data[4:12])[0] + microseconds = struct.unpack(">I", ext.data[0:4])[0] // 1000 + else: + raise UnsupportedTimestampException( + "unsupported timestamp with data length %d" % len(ext.data)) + + return _epoch + datetime.timedelta(seconds=seconds, + microseconds=microseconds) + + def _unpack_array(code, fp, options): if (ord(code) & 0xf0) == 0x90: length = (ord(code) & ~0xf0) @@ -801,6 +876,8 @@ def _unpack2(fp, **options): Insufficient data to unpack the serialized object. InvalidStringException(UnpackException): Invalid UTF-8 string encountered during unpacking. + UnsupportedTimestampException(UnpackException): + Unsupported timestamp format encountered during unpacking. ReservedCodeException(UnpackException): Reserved code encountered during unpacking. UnhashableKeyException(UnpackException): @@ -843,6 +920,8 @@ def _unpack3(fp, **options): Insufficient data to unpack the serialized object. InvalidStringException(UnpackException): Invalid UTF-8 string encountered during unpacking. + UnsupportedTimestampException(UnpackException): + Unsupported timestamp format encountered during unpacking. ReservedCodeException(UnpackException): Reserved code encountered during unpacking. UnhashableKeyException(UnpackException): @@ -888,6 +967,8 @@ def _unpackb2(s, **options): Insufficient data to unpack the serialized object. InvalidStringException(UnpackException): Invalid UTF-8 string encountered during unpacking. + UnsupportedTimestampException(UnpackException): + Unsupported timestamp format encountered during unpacking. ReservedCodeException(UnpackException): Reserved code encountered during unpacking. UnhashableKeyException(UnpackException): @@ -934,6 +1015,8 @@ def _unpackb3(s, **options): Insufficient data to unpack the serialized object. InvalidStringException(UnpackException): Invalid UTF-8 string encountered during unpacking. + UnsupportedTimestampException(UnpackException): + Unsupported timestamp format encountered during unpacking. ReservedCodeException(UnpackException): Reserved code encountered during unpacking. UnhashableKeyException(UnpackException): @@ -966,6 +1049,8 @@ def __init(): global load global loads global compatibility + global _epoch + global _utc_tzinfo global _float_precision global _unpack_dispatch_table global xrange @@ -973,6 +1058,14 @@ def __init(): # Compatibility mode for handling strings/bytes with the old specification compatibility = False + if sys.version_info[0] == 3: + _utc_tzinfo = datetime.timezone.utc + else: + _utc_tzinfo = None + + # Calculate epoch datetime + _epoch = datetime.datetime(1970, 1, 1, tzinfo=_utc_tzinfo) + # Auto-detect system float precision if sys.float_info.mant_dig == 53: _float_precision = "double" diff --git a/gs9o.py b/gs9o.py index 39be5bee..ecafffe7 100644 --- a/gs9o.py +++ b/gs9o.py @@ -4,6 +4,8 @@ from .gosubl import gsshell from .gosubl import mg9 from .gosubl import sh +from .gosubl.margo import mg +from .gosubl.margo_state import actions import datetime import json import os @@ -18,9 +20,9 @@ DOMAIN = "9o" AC_OPTS = sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS SPLIT_FN_POS_PAT = re.compile(r'(.+?)(?:[:](\d+))?(?:[:](\d+))?$') -URL_SCHEME_PAT = re.compile(r'^[\w.+-]+://') -URL_PATH_PAT = re.compile(r'^(?:[\w.+-]+://|(?:www|(?:\w+\.)*(?:golang|pkgdoc|gosublime)\.org))') -HIST_EXPAND_PAT = re.compile(r'^(\^+)\s*(\d+)$') +URL_SCHEME_PAT = re.compile(r'[\w.+-]+://') +URL_PATH_PAT = re.compile(r'(?:[\w.+-]+://|(?:www|(?:\w+\.)*(?:golang|pkgdoc|gosublime)\.org))') +HIST_EXPAND_PAT = re.compile(r'^[\'"\s]*(\^+)\s*(\d+)[\'"\s]*$') HOURGLASS = u'\u231B' @@ -30,9 +32,6 @@ 'build', 'replay', 'clear', - 'tskill', - 'tskill replay', - 'tskill go', 'go', 'go build', 'go clean', @@ -58,25 +57,45 @@ ] DEFAULT_CL = [(s, s+' ') for s in DEFAULT_COMMANDS] -stash = {} -tid_alias = {} +try: + stash +except NameError: + stash = {} + +try: + tid_alias +except NameError: + tid_alias = {} def active_wd(win=None): _, v = gs.win_view(win=win) return gs.basedir_or_cwd(v.file_name() if v else '') +_9o_instance_default = '9o' + +def _9o_instance(wd): + name = gs.setting('9o_instance') or _9o_instance_default + if name == 'auto': + name = wd or name + + return name.replace('#', '~') + +def _rkey(wd): + _, rkey = mg.run_tokens.next() + return rkey.replace('#', '~') + +def _rcmd_wdid_rkey(*, fd): + l = fd.split('#', 1) + return (l[0], l[1]) if len(l) == 2 else (_wdid(_9o_instance_default), l[0]) + +def _rcmd_fd(*, wd, rkey): + return '%s#%s' % (_wdid(wd), rkey) + def _hkey(wd): - name = gs.setting("9o_instance") - if name: - wd = name - return '9o.hist.%s' % wd + return '9o.hist.%s' % _9o_instance(wd) def _wdid(wd): - name = gs.setting("9o_instance") - if name: - return name - return '9o://%s' % wd - + return '9o://%s' % _9o_instance(wd) class EV(sublime_plugin.EventListener): def on_query_completions(self, view, prefix, locations): @@ -164,6 +183,7 @@ def run(self, edit, up): class Gs9oInitCommand(sublime_plugin.TextCommand): def run(self, edit, wd=None): v = self.view + mg.view(v.id(), view=v).is_9o = True vs = v.settings() if not wd: @@ -232,45 +252,62 @@ def run(self, edit, wd=None): os.chdir(wd) class Gs9oOpenCommand(sublime_plugin.TextCommand): - def run(self, edit, wd=None, run=[], save_hist=False, focus_view=True): - self.view.window().run_command('gs9o_win_open', { - 'wd': wd, - 'run': run, - 'save_hist': save_hist, - 'focus_view': focus_view, - }) + def run(self, edit, **kw): + win = self.view.window() or sublime.active_window() + win.run_command('gs9o_win_open', kw) class Gs9oWinOpenCommand(sublime_plugin.WindowCommand): - def run(self, wd=None, run=[], save_hist=False, focus_view=True): + def run( + self, + wd = None, + run = [], + save_hist = False, + focus_view = True, + show_view = True, + env = {}, + push_output = [], + wdid = '', + action_data={}, + ): win = self.window wid = win.id() if not wd: wd = active_wd(win=win) - id = _wdid(wd) + id = wdid or _wdid(wd) st = stash.setdefault(wid, {}) v = st.get(id) if v is None: v = win.get_output_panel(id) st[id] = v - win.run_command("show_panel", {"panel": ("output.%s" % id)}) + if show_view: + win.run_command("show_panel", {"panel": ("output.%s" % id)}) if focus_view: win.focus_view(v) - v.run_command('gs9o_init', {'wd': wd}) + if not push_output: + v.run_command('gs9o_init', {'wd': wd}) + + if push_output: + v.run_command('gs9o_push_output', push_output) if run: - v.run_command('gs9o_paste_exec', {'cmd': ' '.join(run), 'save_hist': save_hist}) + v.run_command('gs9o_paste_exec', { + 'cmd': ' '.join(shlex.quote(s) for s in run), + 'save_hist': save_hist, + 'env': env, + 'action_data': action_data, + }) class Gs9oPasteExecCommand(sublime_plugin.TextCommand): - def run(self, edit, cmd, save_hist=False): + def run(self, edit, cmd, save_hist=False, env={}, action_data={}): view = self.view view.insert(edit, view.line(view.size()-1).end(), cmd) view.sel().clear() view.sel().add(view.line(view.size()-1).end()) - view.run_command('gs9o_exec', {'save_hist': save_hist}) + view.run_command('gs9o_exec', {'save_hist': save_hist, 'env': env, 'action_data': action_data}) class Gs9oOpenSelectionCommand(sublime_plugin.TextCommand): def is_enabled(self): @@ -309,20 +346,20 @@ def act_on_path(view, path): row = 0 col = 0 - m = gs.VFN_ID_PAT.match(path) + m = gs.VFN_ID_PAT.search(path) if m: path = 'gs.view://%s' % m.group(1) - m2 = gs.ROWCOL_PAT.match(m.group(2)) + m2 = gs.ROWCOL_PAT.search(m.group(2)) if m2: row = int(m2.group(1))-1 if m2.group(1) else 0 col = int(m2.group(2))-1 if m2.group(2) else 0 else: - if URL_PATH_PAT.match(path): + if URL_PATH_PAT.search(path): if path.lower().startswith('gs.packages://'): path = os.path.join(gs.packages_dir(), path[14:]) else: try: - if not URL_SCHEME_PAT.match(path): + if not URL_SCHEME_PAT.search(path): path = 'http://%s' % path gs.notify(DOMAIN, 'open url: %s' % path) webbrowser.open_new_tab(path) @@ -346,18 +383,12 @@ def act_on_path(view, path): return False - -def _exparg(s, m): - s = string.Template(s).safe_substitute(m) - s = os.path.expanduser(s) - return s - class Gs9oExecCommand(sublime_plugin.TextCommand): def is_enabled(self): pos = gs.sel(self.view).begin() return self.view.score_selector(pos, 'text.9o') > 0 - def run(self, edit, save_hist=False): + def run(self, edit, save_hist=False, env={}, action_data={}): view = self.view pos = gs.sel(view).begin() line = view.line(pos) @@ -402,12 +433,14 @@ def run(self, edit, save_hist=False): view.run_command('gs9o_init') return + rkey = _rkey(wd) + # view.add_regions(rkey, [sublime.Region(line.begin(), view.size())], '') + view.add_regions(rkey, [line], '') view.replace(edit, line, (u'[ `%s` %s ]' % (cmd, HOURGLASS))) - rkey = '9o.exec.%s' % uuid.uuid4() - view.add_regions(rkey, [sublime.Region(line.begin(), view.size())], '') view.run_command('gs9o_init') nv = sh.env() + nv.update(env) anv = nv.copy() seen = {} am = aliases() @@ -432,56 +465,80 @@ def run(self, edit, save_hist=False): cmd = string.Template(alias).safe_substitute(anv) if nm != 'sh': + args = [] + if ag: + args = shlex.split(gs.astr(ag)) + f = builtins().get(nm) if f: - args = [] - if ag: - args = [_exparg(s, nv) for s in shlex.split(gs.astr(ag))] - - f(view, edit, args, wd, rkey) + try: + f(view, edit, args, wd, rkey, action_data=action_data) + except TypeError: + f(view, edit, args, wd, rkey) + return + else: + _rcmd( + view=view, + edit=edit, + name=nm, + args=args, + wd=wd, + rkey=rkey, + action_data=action_data, + ) return if nm == 'sh': args = sh.cmd(ag) + cmd_sh(view, edit, args, wd, rkey) else: args = sh.cmd(cmd) - - cmd_sh(view, edit, args, wd, rkey) else: view.insert(edit, gs.sel(view).begin(), '\n') class Gs9oPushOutput(sublime_plugin.TextCommand): - def run(self, edit, rkey, output, hourglass_repl=''): + def run(self, edit, rkey, output, hourglass_repl='', done=True): view = self.view output = '\t%s' % gs.ustr(output).strip().replace('\r', '').replace('\n', '\n\t') + xpos, vpos = view.viewport_position() + ypos = view.layout_extent()[1] - vpos regions = view.get_regions(rkey) if regions: - line = view.line(regions[0].begin()) - lsrc = view.substr(line).replace(HOURGLASS, (hourglass_repl or '| done')) - view.replace(edit, line, lsrc) - r = line + prompt = view.line(regions[0].begin()) + if done: + lsrc = view.substr(prompt).replace(HOURGLASS, (hourglass_repl or '| done')) + view.replace(edit, prompt, lsrc) + + regions = view.get_regions(rkey) + r = view.line(regions[-1].end()) if output.strip(): - line = view.line(regions[0].begin()) - view.insert(edit, line.end(), '\n%s' % output) - r = view.get_regions(rkey)[0] + n = view.insert(edit, r.end(), '\n%s' % output) + r = sublime.Region(prompt.begin(), r.end() + n) + view.erase_regions(rkey) + view.add_regions(rkey, [r]) else: n = view.size() view.insert(edit, n, '\n%s' % output) r = sublime.Region(n, view.size()) - if gs.setting('9o_show_end') is True: - view.show(r.end()) - else: - view.show(r.begin()) + if done: + if gs.setting('9o_show_end') is True: + view.show(r.end(), True) + else: + view.show(r.begin(), True) + elif gs.sel(view).begin() >= r.begin(): + ypos = view.layout_extent()[1] - ypos + view.set_viewport_position((xpos, ypos), False) class Gs9oRunManyCommand(sublime_plugin.TextCommand): - def run(self, edit, wd=None, commands=[], save_hist=False, focus_view=False): + def run(self, edit, wd=None, commands=[], save_hist=False, focus_view=False, show_view=True): for run in commands: self.view.run_command("gs9o_open", { 'run': run, 'wd': wd, 'save_hist': save_hist, 'focus_view': focus_view, + 'show_view': show_view, }) def aliases(): @@ -499,15 +556,13 @@ def builtins(): return m -def push_output(view, rkey, output, hourglass_repl=''): - def f(): - view.run_command('gs9o_push_output', { - 'rkey': rkey, - 'output': output, - 'hourglass_repl': hourglass_repl, - }) - - sublime.set_timeout(f, 0) +def push_output(view, rkey, output, hourglass_repl='', done=True): + view.run_command('gs9o_push_output', { + 'rkey': rkey, + 'output': output, + 'hourglass_repl': hourglass_repl, + 'done': done, + }) def _save_all(win, wd): if gs.setting('autosave') is True and win is not None: @@ -515,7 +570,7 @@ def _save_all(win, wd): try: fn = v.file_name() if fn and v.is_dirty() and fn.endswith('.go') and os.path.dirname(fn) == wd: - v.run_command('gs_fmt_save') + v.run_command('save') except Exception: gs.error_traceback(DOMAIN) @@ -550,17 +605,37 @@ def f(): return cid, cb -def cmd_margo_reinstall(view, edit, args, wd, rkey): - def cb(): - gs.del_attr(mg9._inst_name()) - out = mg9.install('', True, True) - gs.notify(DOMAIN, 'MarGo re-installed done') - push_output(view, rkey, out) - - gsq.launch(DOMAIN, cb) - -def cmd_echo(view, edit, args, wd, rkey): - push_output(view, rkey, ' '.join(args)) +def _rcmd_output_handler(rs, act): + wdid, rkey = _rcmd_wdid_rkey(fd=act.fd) + sublime.active_window().run_command('gs9o_win_open', { + 'wdid': wdid, + 'focus_view': False, + 'show_view': False, + 'push_output': { + 'rkey': rkey, + 'output': act.output, + 'done': act.close, + }, + }) + +mg.output_handler = _rcmd_output_handler + +def _rcmd(*, view, edit, name, args, wd, rkey, action_data={}): + def cb(rs): + if rs.error: + push_output(view, rkey, rs.error) + + wd = action_data.get('Dir') or wd + act = actions.RunCmd.copy() + act['Data'] = { + 'Fd': _rcmd_fd(wd=wd, rkey=rkey), + 'Name': name, + 'Args': args, + } + act['Data'].update(action_data) + # `view` is the 9o view, but the command wants the `active/editor view` + run_view = None + mg.send(view=run_view, cb=cb, actions=[act]) def cmd_which(view, edit, args, wd, rkey): l = [] @@ -596,7 +671,9 @@ def cmd_cd(view, edit, args, wd, rkey): wd = args[0] wd = string.Template(wd).safe_substitute(sh.env()) wd = os.path.expanduser(wd) + print('>'+wd) wd = os.path.abspath(wd) + print('<'+wd) else: fn = view.window().active_view().file_name() if fn: @@ -614,24 +691,23 @@ def cmd_reset(view, edit, args, wd, rkey): push_output(view, rkey, '') view.erase(edit, sublime.Region(0, view.size())) view.run_command('gs9o_init') + if args: + view.run_command('gs9o_paste_exec', {'cmd': ' '.join(args), 'save_hist': False}) def cmd_clear(view, edit, args, wd, rkey): cmd_reset(view, edit, args, wd, rkey) -def cmd_go(view, edit, args, wd, rkey): +def cmd_go(view, edit, args, wd, rkey, action_data={}): _save_all(view.window(), wd) - - cid, cb = _9_begin_call('go', view, edit, args, wd, rkey, '9go-%s' % wd) - a = { - 'cid': cid, - 'env': sh.env(), - 'cwd': wd, - 'cmd': { - 'name': 'go', - 'args': args, - } - } - sublime.set_timeout(lambda: mg9.acall('sh', a, cb), 0) + sublime.set_timeout_async(lambda: _rcmd( + view=view, + edit=edit, + name='go', + args=args, + wd=wd, + rkey=rkey, + action_data=action_data, + )) def cmd_cancel_replay(view, edit, args, wd, rkey): cid = '' @@ -649,12 +725,12 @@ def cmd_cancel_replay(view, edit, args, wd, rkey): mg9.acall('kill', {'cid': cid}, None) push_output(view, rkey, '') -def cmd_sh(view, edit, args, wd, rkey): +def cmd_sh(view, edit, args, wd, rkey, action_data={}): cid, cb = _9_begin_call('sh', view, edit, args, wd, rkey, '') a = { 'cid': cid, 'env': sh.env(), - 'cwd': wd, + 'cwd': action_data.get('Dir') or wd, 'cmd': { 'name': args[0], 'args': args[1:], @@ -678,61 +754,16 @@ def cmd_help(view, edit, args, wd, rkey): gs.focus(gs.dist_path('9o.md')) push_output(view, rkey, '') -def cmd_run(view, edit, args, wd, rkey): - cmd_9(view, edit, gs.lst('run', args), wd, rkey) - def cmd_replay(view, edit, args, wd, rkey): - cmd_9(view, edit, gs.lst('replay', args), wd, rkey) - -def cmd_build(view, edit, args, wd, rkey): - cmd_9(view, edit, gs.lst('build', args), wd, rkey) - -def cmd_9(view, edit, args, wd, rkey): - if len(args) == 0 or args[0] not in ('run', 'replay', 'build'): - push_output(view, rkey, ('9: invalid args %s' % args)) - return - - subcmd = args[0] - cid = '' - if subcmd == 'replay': - cid = '9replay-%s' % wd - cid, cb = _9_begin_call(subcmd, view, edit, args, wd, rkey, cid) - - a = { - 'cid': cid, - 'env': sh.env(), - 'dir': wd, - 'args': args[1:], - 'build_only': (subcmd == 'build'), - } - - win = view.window() - if win is not None: - av = win.active_view() - if av is not None: - fn = av.file_name() - if fn: - _save_all(win, wd) - else: - if gs.is_go_source_view(av, False): - a['fn'] = gs.view_fn(av) - a['src'] = av.substr(sublime.Region(0, av.size())) - - sublime.set_timeout(lambda: mg9.acall('play', a, cb), 0) - -def cmd_tskill(view, edit, args, wd, rkey): - if len(args) == 0: - sublime.set_timeout(lambda: sublime.active_window().run_command("gs_show_tasks"), 0) - push_output(view, rkey, '') - return - - l = [] - for tid in args: - tid = tid.lstrip('#') - tid = tid_alias.get('%s-%s' % (tid, wd), tid) - l.append('kill %s: %s' % (tid, ('yes' if gs.cancel_task(tid) else 'no'))) - - push_output(view, rkey, '\n'.join(l)) + _save_all(view.window(), wd) + _rcmd( + view=view, + edit=edit, + name='go.replay', + args=args, + wd=wd, + rkey=rkey, + ) def _env_settings(d, view, edit, args, wd, rkey): if len(args) > 0: diff --git a/gscommands.py b/gscommands.py index 90518880..82df256b 100644 --- a/gscommands.py +++ b/gscommands.py @@ -82,7 +82,9 @@ def run(self, edit, row, col=0): r = sublime.Region(pt, pt) self.view.sel().clear() self.view.sel().add(r) - self.view.show(pt) + self.view.show(pt, True) + xpos, ypos = self.view.viewport_position() + self.view.set_viewport_position((0, ypos), False) dmn = 'gs.focus.%s:%s:%s' % (gs.view_fn(self.view), row, col) flags = sublime.DRAW_EMPTY_AS_OVERWRITE show = lambda: self.view.add_regions(dmn, [r], 'comment', 'bookmark', flags) @@ -117,7 +119,7 @@ def run(self): gs.error_traceback('GsNewGoFile') self.window.new_file().run_command('gs_create_new_go_file', { - 'pkg_name': pkg_name, + 'pkg_name': '', 'file_name': 'main.go', }) @@ -126,9 +128,17 @@ def run(self, edit, pkg_name, file_name): view = self.view view.set_name(file_name) view.set_syntax_file(gs.tm_path('go')) - view.replace(edit, sublime.Region(0, view.size()), 'package %s\n' % pkg_name) - view.sel().clear() - view.sel().add(view.find(pkg_name, 0, sublime.LITERAL)) + if pkg_name == '': + view.sel().add(sublime.Region(0, 0)) + view.run_command('auto_complete', { + 'api_completions_only': True, + 'disable_auto_insert': True, + 'next_completion_if_showing': False, + }) + else: + view.replace(edit, sublime.Region(0, view.size()), 'package %s\n' % pkg_name) + view.sel().clear() + view.sel().add(view.find(pkg_name, 0, sublime.LITERAL)) class GsShowTasksCommand(sublime_plugin.WindowCommand): def run(self): diff --git a/gsev.py b/gsev.py index a0dfdb66..554605f8 100644 --- a/gsev.py +++ b/gsev.py @@ -6,36 +6,6 @@ DOMAIN = 'GsEV' -class UncleSam(object): - def __init__(self): - self.phantoms = None - - def on_load(self, view): - if view.file_name() != gs.dist_path('CHANGELOG.md'): - return - - self.phantoms = sublime.PhantomSet(view, 'gs.uncle-sam') - self.phantoms.update([sublime.Phantom( - sublime.Region(-1, -1), - ''' - - - - - - '''.format( - url='https://margo.sh/gosublime-future', - src=gs.dist_path('images/fight-the-future.png') - ), - sublime.LAYOUT_INLINE, - self._on_click - )]) - - def _on_click(self, url): - webbrowser.open_new_tab(url) - -uncle_sam = UncleSam() - class EV(sublime_plugin.EventListener): def on_pre_save(self, view): view.run_command('gs_fmt') @@ -55,14 +25,16 @@ def on_activated(self, view): def on_load(self, view): sublime.set_timeout(lambda: do_set_gohtml_syntax(view), 0) - sublime.set_timeout_async(lambda: uncle_sam.on_load(view), 0) class GsOnLeftClick(sublime_plugin.TextCommand): def run(self, edit): view = self.view if gs.is_go_source_view(view): - if not gstest.handle_action(view, 'left-click'): - view.run_command('gs_doc', {"mode": "goto"}) + view.run_command('gs9o_open', { + "run": [".actuate", "-button=left"], + "focus_view": False, + "show_view": False, + }) elif view.score_selector(gs.sel(view).begin(), "text.9o") > 0: view.window().run_command("gs9o_open_selection") @@ -70,8 +42,11 @@ class GsOnRightClick(sublime_plugin.TextCommand): def run(self, edit): view = self.view if gs.is_go_source_view(view): - if not gstest.handle_action(view, 'right-click'): - view.run_command('gs_doc', {"mode": "hint"}) + view.run_command('gs9o_open', { + "run": [".actuate", "-button=right"], + "focus_view": False, + "show_view": False, + }) def do_post_save(view): if not gs.is_pkg_view(view): diff --git a/something_borrowed/Go/Go.sublime-syntax b/something_borrowed/Go/Go-Copy.sublime-syntax similarity index 99% rename from something_borrowed/Go/Go.sublime-syntax rename to something_borrowed/Go/Go-Copy.sublime-syntax index 56e5c7bf..1d2815b3 100644 --- a/something_borrowed/Go/Go.sublime-syntax +++ b/something_borrowed/Go/Go-Copy.sublime-syntax @@ -1,7 +1,7 @@ %YAML 1.2 --- # http://www.sublimetext.com/docs/3/syntax.html -name: Go +name: 'GoSublime: Go (Copy)' file_extensions: - go first_line_match: "-[*]-( Mode:)? Go -[*]-" diff --git a/something_borrowed/Go/generate.go b/something_borrowed/Go/generate.go index 47a906b6..fae7048b 100644 --- a/something_borrowed/Go/generate.go +++ b/something_borrowed/Go/generate.go @@ -6,6 +6,7 @@ package main import ( + "bytes" "fmt" "io/ioutil" "net/http" @@ -18,6 +19,7 @@ type dlFile struct { name string url string dirs []string + filt func(s []byte) []byte } func main() { @@ -36,6 +38,9 @@ func main() { name: "Go.sublime-syntax", url: "https://raw.githubusercontent.com/sublimehq/Packages/master/Go/Go.sublime-syntax", dirs: []string{"."}, + filt: func(s []byte) []byte { + return bytes.Replace(s, []byte("name: Go"), []byte("name: 'GoSublime: Go (Copy)'"), -1) + }, }, } for _, f := range urls { @@ -64,6 +69,10 @@ func dl(f dlFile) { return } + if f.filt != nil { + content = f.filt(content) + } + for _, dir := range f.dirs { ioutil.WriteFile(filepath.Join(dir, f.name), content, 0644) if err != nil { diff --git a/src/disposa.blue/margo/.travis.yml b/src/disposa.blue/margo/.travis.yml deleted file mode 100644 index 71c75fc7..00000000 --- a/src/disposa.blue/margo/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -go_import_path: disposa.blue/margo -language: go -go: - - 1.9 - - 1.x - -script: - - go test -race -v ./... diff --git a/src/disposa.blue/margo/Gopkg.lock b/src/disposa.blue/margo/Gopkg.lock deleted file mode 100644 index 3a538bbd..00000000 --- a/src/disposa.blue/margo/Gopkg.lock +++ /dev/null @@ -1,27 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - name = "github.com/ugorji/go" - packages = ["codec"] - revision = "16f09ef744fd4227190f626f14cfdefb14362b3b" - -[[projects]] - name = "github.com/urfave/cli" - packages = ["."] - revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" - version = "v1.20.0" - -[[projects]] - branch = "master" - name = "golang.org/x/crypto" - packages = ["blake2b"] - revision = "91a49db82a88618983a78a06c1cbd4e00ab749ab" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "abcbbea5d46799cef82db8ef20f2f68d7d9580d20996b07df2f68d66cad13a49" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/src/disposa.blue/margo/Gopkg.toml b/src/disposa.blue/margo/Gopkg.toml deleted file mode 100644 index 46f81a2c..00000000 --- a/src/disposa.blue/margo/Gopkg.toml +++ /dev/null @@ -1,15 +0,0 @@ -[[constraint]] - name = "github.com/ugorji/go" - branch = "master" - -[[constraint]] - name = "github.com/urfave/cli" - version = "1.20.0" - -[[constraint]] - branch = "master" - name = "golang.org/x/crypto" - -[prune] - go-tests = true - unused-packages = true diff --git a/src/disposa.blue/margo/cmd/margo/main.go b/src/disposa.blue/margo/cmd/margo/main.go deleted file mode 100644 index a93d4f8c..00000000 --- a/src/disposa.blue/margo/cmd/margo/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "disposa.blue/margo/cmdpkg/margo" -) - -func main() { - margo.Main() -} diff --git a/src/disposa.blue/margo/cmdpkg/margo/main.go b/src/disposa.blue/margo/cmdpkg/margo/main.go deleted file mode 100644 index f9661961..00000000 --- a/src/disposa.blue/margo/cmdpkg/margo/main.go +++ /dev/null @@ -1,15 +0,0 @@ -package margo - -import ( - "disposa.blue/margo/mgcli" - "disposa.blue/margo/sublime" - "github.com/urfave/cli" -) - -func Main() { - app := mgcli.NewApp() - app.Commands = []cli.Command{ - sublime.Command, - } - app.RunAndExitOnError() -} diff --git a/src/disposa.blue/margo/extension-example/extension-example.go b/src/disposa.blue/margo/extension-example/extension-example.go deleted file mode 100644 index 5c07e51e..00000000 --- a/src/disposa.blue/margo/extension-example/extension-example.go +++ /dev/null @@ -1,124 +0,0 @@ -package margo - -import ( - "disposa.blue/margo/golang" - "disposa.blue/margo/mg" - "time" -) - -// Margo is the entry-point to margo -func Margo(ma mg.Args) { - // add our reducers (margo plugins) to the store - // they are run in the specified order - // and should ideally not block for more than a couple milliseconds - ma.Store.Use( - // by default, events (e.g. ViewSaved) are triggered in all files - // uncomment the reducer below to restict event to Go(-lang) files - // please note, however, that this mode is not tested - // and saving a non-go file will not trigger linters, etc. for that go pkg - // - // mg.Reduce(func(mx *mg.Ctx) *mg.State { - // return mx.SetConfig(mx.Config.EnabledForLangs("go")) - // }), - - // add the day and time to the status bar - // DayTimeStatus, - - // both GoFmt and GoImports will automatically disable the GoSublime version - // you will need to install the `goimports` tool manually - // https://godoc.org/golang.org/x/tools/cmd/goimports - // - // golang.GoFmt, - // or - // golang.GoImports, - - // use gocode for autocompletion - &golang.Gocode{ - // automatically install missing packages - // Autobuild: true, - - // autocompete packages that are not yet imported - // this goes well with GoImports - UnimportedPackages: true, - - // show the function parameters. this can take up a lot of space - ShowFuncParams: true, - }, - - // add some default context aware-ish snippets - golang.Snippets, - - // add our own snippets - - // check the file for syntax errors - &golang.SyntaxCheck{}, - - // add our own snippets - MySnippets, - - // run `go install` on save - // or use GoInstallDiscardBinaries which will additionally set $GOBIN - // to a temp directory so binaries are not installed into your $PATH - // - // golang.GoInstall(), - // or - // golang.GoInstallDiscardBinaries(), - - // run `go vet` on save. go vet is ran automatically as part of `go test` in go1.10 - // golang.GoVet(), - - // run `go test -race` on save - // in go1.10, go vet is ran automatically - golang.GoTest("-race"), - - // run `golint` on save - // &golang.Linter{Name: "golint", Label: "Go/Lint"}, - - // run gometalinter on save - // &golang.Linter{Name: "gometalinter", Args: []string{ - // "--disable=gas", - // "--fast", - // }}, - ) -} - -// DayTimeStatus adds the current day and time to the status bar -var DayTimeStatus = mg.Reduce(func(mx *mg.Ctx) *mg.State { - if _, ok := mx.Action.(mg.Started); ok { - dispatch := mx.Store.Dispatch - // kick off the ticker when we start - go func() { - ticker := time.NewTicker(1 * time.Second) - for range ticker.C { - dispatch(mg.Render) - } - }() - } - - // we always want to render the time - // otherwise it will sometimes disappear from the status bar - now := time.Now() - format := "Mon, 15:04" - if now.Second()%2 == 0 { - format = "Mon, 15 04" - } - return mx.AddStatus(now.Format(format)) -}) - -// MySnippets is a slice of functions returning our own snippets -var MySnippets = golang.SnippetFuncs{ - func(cx *golang.CompletionCtx) []mg.Completion { - // if we're not in a block (i.e. function), do nothing - if !cx.Scope.Is(golang.BlockScope) { - return nil - } - - return []mg.Completion{ - { - Query: "if err", - Title: "err != nil { return }", - Src: "if ${1:err} != nil {\n\treturn $0\n}", - }, - } - }, -} diff --git a/src/disposa.blue/margo/golang/common.go b/src/disposa.blue/margo/golang/common.go deleted file mode 100644 index fd889529..00000000 --- a/src/disposa.blue/margo/golang/common.go +++ /dev/null @@ -1,178 +0,0 @@ -package golang - -import ( - "disposa.blue/margo/mg" - "go/ast" - "go/build" - "go/token" - "os" - "path/filepath" - "reflect" - "regexp" - "strings" - "unicode" - "unicode/utf8" -) - -var ( - CommonPatterns = append([]*regexp.Regexp{ - regexp.MustCompile(`^\s*(?P.+?\.\w+):(?P\d+:)(?P\d+:?)?(?:(?Pwarning|error)[:])?(?P.+?)(?: [(](?P