Skip to content

Commit

Permalink
Get editor opening working with dash updates
Browse files Browse the repository at this point in the history
  • Loading branch information
DomBlack committed Sep 8, 2023
1 parent bffebdd commit cffad36
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 13 deletions.
31 changes: 23 additions & 8 deletions cli/daemon/dash/dash.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,16 @@ func (h *handler) Handle(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2

case "editors/open":
var params struct {
Editor string `json:"editor"`
File string `json:"file"`
Line int `json:"line,omitempty"`
AppID string `json:"app_id"`
Editor string `json:"editor"`
File string `json:"file"`
StartLine int `json:"start_line,omitempty"`
StartCol int `json:"start_col,omitempty"`
EndLine int `json:"end_line,omitempty"`
EndCol int `json:"end_col,omitempty"`
}
if err := unmarshal(&params); err != nil {
log.Warn().Err(err).Msg("dash: could not parse open command")
return reply(ctx, nil, err)
}

Expand All @@ -221,13 +226,23 @@ func (h *handler) Handle(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2
return reply(ctx, nil, err)
}

if err := editors.LaunchExternalEditor(params.File, editor); err != nil {
log.Err(err).Str("editor", params.Editor).Msg("dash: could not open file")
return reply(ctx, nil, err)
runs := h.run.ListRuns()
for _, r := range runs {
if r.App.PlatformOrLocalID() == params.AppID {
params.File = filepath.Join(r.App.Root(), params.File)

if err := editors.LaunchExternalEditor(params.File, params.StartLine, params.StartCol, editor); err != nil {
log.Err(err).Str("editor", params.Editor).Msg("dash: could not open file")
return reply(ctx, nil, err)
}

type openResp struct{}
return reply(ctx, openResp{}, nil)
}
}

type openResp struct{}
return reply(ctx, openResp{}, nil)
log.Warn().Str("app_id", params.AppID).Msg("dash: could not find app")
return reply(ctx, nil, fmt.Errorf("could not find app"))
}

return jsonrpc2.MethodNotFound(ctx, reply, r)
Expand Down
18 changes: 13 additions & 5 deletions pkg/editors/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,42 @@ import (
"runtime"

"github.com/cockroachdb/errors"
"github.com/rs/zerolog/log"
)

// LaunchExternalEditor opens a given file or folder in the desired external editor.
func LaunchExternalEditor(fullPath string, editor FoundEditor) error {
func LaunchExternalEditor(fullPath string, startLine int, startCol int, editor FoundEditor) error {
_, err := os.Stat(editor.Path)
if os.IsNotExist(err) {

Check failure on line 15 in pkg/editors/launch.go

View workflow job for this annotation

GitHub Actions / Static Analysis

[semgrep] New code should use errors.Is with the appropriate error type
return errors.Wrapf(err, "editor %s not found", editor.Editor)
}

// Encore patch to allow opening to a specific line and column in the file
// if supported by the IDE
toExecute := convertFilePathToURLScheme(editor.Editor, fullPath, startLine, startCol)

var cmd *exec.Cmd

//goland:noinspection GoBoolExpressions
if editor.UsesShell {
if runtime.GOOS == "windows" {
cmd = exec.Command("cmd.exe", "/c", editor.Path, fullPath)
cmd = exec.Command("cmd.exe", "/c", editor.Path, toExecute)
} else {
cmd = exec.Command("sh", "-c", editor.Path+" "+fullPath)
cmd = exec.Command("sh", "-c", editor.Path+" "+toExecute)

Check failure on line 30 in pkg/editors/launch.go

View workflow job for this annotation

GitHub Actions / Static Analysis

[semgrep] Detected non-static command inside Command. Audit the input to 'exec.Command'. If unverified user data can reach this call site, this is a code injection vulnerability. A malicious actor can inject a malicious script to execute arbitrary code.
}
} else if runtime.GOOS == "darwin" {
cmd = exec.Command("open", "-a", editor.Path, fullPath)
// open has different semantics on macOS, so we need to handle it differently
cmd = exec.Command("open", "-a", editor.Path, toExecute)
} else {
cmd = exec.Command(editor.Path, fullPath)
cmd = exec.Command(editor.Path, toExecute)

Check failure on line 36 in pkg/editors/launch.go

View workflow job for this annotation

GitHub Actions / Static Analysis

[semgrep] Detected non-static command inside Command. Audit the input to 'exec.Command'. If unverified user data can reach this call site, this is a code injection vulnerability. A malicious actor can inject a malicious script to execute arbitrary code.
}

// Make sure the editor processes are detached from the Encore daemon.
// Otherwise, some editors (like Notepad++) will be killed when the
// Encore daemon shutsdown.
cmd.SysProcAttr = detachSysProcAttr()

log.Info().Str("full_path", fullPath).Str("editor", editor.Editor).Str("cmd", cmd.String()).Msg("attempting to open file")

return errors.Wrap(cmd.Start(), "failed to start editor")
}
5 changes: 5 additions & 0 deletions pkg/editors/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package editors
import (
"context"
goerrors "errors"
"sort"
"strings"
"sync"

Expand Down Expand Up @@ -42,6 +43,10 @@ func Resolve(ctx context.Context) ([]FoundEditor, error) {
if err != nil {
return nil, errors.Wrap(err, "unable to get available editors")
}

sort.Slice(editorCache, func(i, j int) bool {
return editorCache[i].Editor < editorCache[j].Editor
})
}

return editorCache, nil
Expand Down
73 changes: 73 additions & 0 deletions pkg/editors/scheme_protocols.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package editors

import (
"fmt"
"net/url"
"strings"
)

/*
This file was added by Encore and is not part of the original GitHub Desktop codebase
*/

func convertFilePathToURLScheme(editorName string, fullPath string, startLine int, startCol int) string {
switch strings.ToLower(editorName) {
case "vscode", "visual studio code", "visual studio code (insiders)":
if startLine > 0 {
fullPath = fmt.Sprintf("%s:%d", fullPath, startLine)
}
return toURLScheme("vscode", "file", fullPath, "", "", 0, 0)
case "jetbrains goland", "goland":
return toJetBrainsScheme("goland", fullPath, startLine, startCol)
case "jetbrains phpstorm", "phpstorm":
return toJetBrainsScheme("phpstorm", fullPath, startLine, startCol)
case "jetbrains pycharm", "pycharm", "pycharm community edition":
return toJetBrainsScheme("pycharm", fullPath, startLine, startCol)
case "jetbrains rubymine", "rubymine":
return toJetBrainsScheme("rubymine", fullPath, startLine, startCol)
case "jetbrains webstorm", "webstorm":
return toJetBrainsScheme("webstorm", fullPath, startLine, startCol)
case "jetbrains intellij", "intellij", "idea", "intellij idea", "intellij idea community edition":
return toJetBrainsScheme("idea", fullPath, startLine, startCol)
case "jetbrains clion", "clion":
return toJetBrainsScheme("clion", fullPath, startLine, startCol)
case "textmate", "mate":
return toOpenURLScheme("txmt", "", fullPath, startLine, startCol)
case "bbedit":
return toOpenURLScheme("bbedit", "", fullPath, startLine, startCol)
default:
return fullPath
}
}

func toJetBrainsScheme(scheme string, file string, line int, col int) string {
return toURLScheme(scheme, "open", "", "file", file, line, col)
}

func toOpenURLScheme(scheme string, basePath string, file string, line int, col int) string {
return toURLScheme(scheme, "open", basePath, "url", fmt.Sprintf("file://%s", file), line, col)
}

func toURLScheme(scheme string, host string, basePath string, fileKey string, file string, line int, col int) string {
u := &url.URL{
Scheme: scheme,
Host: host,
Path: basePath,
}

q := u.Query()
if fileKey != "" && file != "" {
q.Set(fileKey, file)
}
if line > 0 {
q.Set("line", fmt.Sprintf("%d", line))

if col > 0 {
q.Set("col", fmt.Sprintf("%d", col))
}
}

u.RawQuery = q.Encode()

return u.String()
}

0 comments on commit cffad36

Please sign in to comment.