mirror of
https://github.com/syncthing/syncthing.git
synced 2024-11-16 18:41:59 -07:00
119 lines
2.5 KiB
Go
119 lines
2.5 KiB
Go
// Copyright (C) 2019 The Syncthing Authors.
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
urlPrefix = "https://raw.githubusercontent.com/syncthing/syncthing/"
|
|
httpTimeout = 10 * time.Second
|
|
)
|
|
|
|
type githubSourceCodeLoader struct {
|
|
mut sync.Mutex
|
|
version string
|
|
cache map[string]map[string][][]byte // version -> file -> lines
|
|
client *http.Client
|
|
}
|
|
|
|
func newGithubSourceCodeLoader() *githubSourceCodeLoader {
|
|
return &githubSourceCodeLoader{
|
|
cache: make(map[string]map[string][][]byte),
|
|
client: &http.Client{Timeout: httpTimeout},
|
|
}
|
|
}
|
|
|
|
func (l *githubSourceCodeLoader) LockWithVersion(version string) {
|
|
l.mut.Lock()
|
|
l.version = version
|
|
if _, ok := l.cache[version]; !ok {
|
|
l.cache[version] = make(map[string][][]byte)
|
|
}
|
|
}
|
|
|
|
func (l *githubSourceCodeLoader) Unlock() {
|
|
l.mut.Unlock()
|
|
}
|
|
|
|
func (l *githubSourceCodeLoader) Load(filename string, line, context int) ([][]byte, int) {
|
|
filename = filepath.ToSlash(filename)
|
|
lines, ok := l.cache[l.version][filename]
|
|
if !ok {
|
|
// Cache whatever we managed to find (or nil if nothing, so we don't try again)
|
|
defer func() {
|
|
l.cache[l.version][filename] = lines
|
|
}()
|
|
|
|
knownPrefixes := []string{"/lib/", "/cmd/"}
|
|
var idx int
|
|
for _, pref := range knownPrefixes {
|
|
idx = strings.Index(filename, pref)
|
|
if idx >= 0 {
|
|
break
|
|
}
|
|
}
|
|
if idx == -1 {
|
|
return nil, 0
|
|
}
|
|
|
|
url := urlPrefix + l.version + filename[idx:]
|
|
resp, err := l.client.Get(url)
|
|
|
|
if err != nil {
|
|
fmt.Println("Loading source:", err)
|
|
return nil, 0
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
fmt.Println("Loading source:", resp.Status)
|
|
return nil, 0
|
|
}
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
_ = resp.Body.Close()
|
|
if err != nil {
|
|
fmt.Println("Loading source:", err.Error())
|
|
return nil, 0
|
|
}
|
|
lines = bytes.Split(data, []byte{'\n'})
|
|
}
|
|
|
|
return getLineFromLines(lines, line, context)
|
|
}
|
|
|
|
func getLineFromLines(lines [][]byte, line, context int) ([][]byte, int) {
|
|
if lines == nil {
|
|
// cached error from ReadFile: return no lines
|
|
return nil, 0
|
|
}
|
|
|
|
line-- // stack trace lines are 1-indexed
|
|
start := line - context
|
|
var idx int
|
|
if start < 0 {
|
|
start = 0
|
|
idx = line
|
|
} else {
|
|
idx = context
|
|
}
|
|
end := line + context + 1
|
|
if line >= len(lines) {
|
|
return nil, 0
|
|
}
|
|
if end > len(lines) {
|
|
end = len(lines)
|
|
}
|
|
return lines[start:end], idx
|
|
}
|