turbo-editors/turbo-corepublic Fork 0
v1.0.0
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

📦 Turbo Core f3ade8d · on v1.0.0 · k33g · 11h ago
root.go · 76 lines · 2.3 KBGo Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// Where a project starts: the directory the language server is given as its
// workspace root.

package app

import (
	"os"
	"path/filepath"

	"rickub.com/turbo-editors/turbo-core/profile"
)

// ProjectRoot returns the directory the language server should work in: the
// project root above the first file named on the command line, or the working
// directory when none was.
//
// "Project root" means the nearest directory at or above the file holding one
// of the profile's RootMarkers — go.mod for Go, Cargo.toml for Rust. A
// language server given the wrong root loads the wrong package set and then
// answers nothing at all, with no error, which is the single most confusing way
// completion can fail.
//
// This is deliberately **not** the rule the editor uses for "the project"
// elsewhere: settings and the project tree are rooted in the working directory
// with no walk up, because a module has a real boundary and "the project" does
// not. A language server is the one thing that genuinely needs the module's
// boundary, so it is the one thing that walks.
//
//	root := app.ProjectRoot(golang.Profile(), []string{"internal/app/app.go"})
//	editor.StartLanguageServer(ctx, root)
func ProjectRoot(p profile.Profile, files []string) string {
	start, err := os.Getwd()
	if err != nil {
		start = "."
	}
	if len(files) > 0 {
		if absolute, err := filepath.Abs(files[0]); err == nil {
			start = filepath.Dir(absolute)
		}
	}
	return markedRoot(start, p.RootMarkers)
}

// markedRoot walks up from a directory looking for one of the markers, and
// returns the directory it started from when there is none.
//
// Returning the starting directory rather than the filesystem root is what
// keeps a file edited outside any project working: the server is given
// somewhere plausible instead of somewhere absurd.
func markedRoot(start string, markers []string) string {
	if len(markers) == 0 {
		return start
	}

	directory := start
	for {
		if holdsAny(directory, markers) {
			return directory
		}
		parent := filepath.Dir(directory)
		if parent == directory {
			return start
		}
		directory = parent
	}
}

// holdsAny reports whether a directory holds one of the marker files.
func holdsAny(directory string, markers []string) bool {
	for _, marker := range markers {
		if _, err := os.Stat(filepath.Join(directory, marker)); err == nil {
			return true
		}
	}
	return false
}