123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- // 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"
- "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 := io.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
- }
|