new: language auto-detect - /api/guess-language, refresh button, guess on paste
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// guessLang heuristically detects a language from source content.
|
||||
func guessLang(s string) string {
|
||||
src := strings.TrimSpace(s)
|
||||
if src == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// JSON: must start with { or [ and parse
|
||||
if src[0] == '{' || src[0] == '[' {
|
||||
var v any
|
||||
if json.Unmarshal([]byte(src), &v) == nil {
|
||||
return "json"
|
||||
}
|
||||
}
|
||||
|
||||
rules := []struct {
|
||||
lang string
|
||||
re *regexp.Regexp
|
||||
}{
|
||||
{"python", regexp.MustCompile(`(?m)^\s*(def |class |import |from \w+ import |@decorator)`)},
|
||||
{"python", regexp.MustCompile(`(^|\n)\s*#!.*python`)},
|
||||
{"go", regexp.MustCompile(`(?m)^\s*(package \w+|import \(|func (\w+|\() )`)},
|
||||
{"rust", regexp.MustCompile(`(?m)(\bfn \w+|let mut \b|\bimpl \b|\bmatch \w+ \{|use std::)`)},
|
||||
{"javascript", regexp.MustCompile(`(?m)(\bconst \w+ = |require\(|import \w+ from |=> \{|\bconsole\.log\()` )},
|
||||
{"java", regexp.MustCompile(`(?m)(\bpublic (static |final |class )|\bSystem\.out\.print|import java\.)`)},
|
||||
{"c", regexp.MustCompile(`(?m)(#include\s*<\w+\.h>|printf\(|\bint main\()` )},
|
||||
{"cpp", regexp.MustCompile(`(?m)(#include\s*<(iostream|vector|string)>|std::|\bcout\s*<<)`)},
|
||||
{"bash", regexp.MustCompile(`(?m)^(#!.*bash|#!.*sh|\w+\(\)\s*\{)` )},
|
||||
{"sql", regexp.MustCompile(`(?i)\b(SELECT .+ FROM|INSERT INTO|CREATE TABLE|UPDATE \w+ SET)\b`)},
|
||||
{"yaml", regexp.MustCompile(`(?m)^(\w[\w-]*:\s*(\||\S)| \w[\w-]*: |---\s*$)`)},
|
||||
{"markdown", regexp.MustCompile("(?m)^(#{1,6} \\S|\\|.*\\||-\\s\\[\\s?\\]|```)")},
|
||||
}
|
||||
|
||||
scores := map[string]int{}
|
||||
for _, r := range rules {
|
||||
n := len(r.re.FindAllString(src, -1))
|
||||
if n > 0 {
|
||||
// later generic rules shouldn't drown out earlier specific ones
|
||||
scores[r.lang] += n
|
||||
}
|
||||
}
|
||||
|
||||
best, bestN := "", 0
|
||||
for lang, n := range scores {
|
||||
if n > bestN {
|
||||
best, bestN = lang, n
|
||||
}
|
||||
}
|
||||
if bestN == 0 {
|
||||
return "text"
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
func (a *apiServer) handleGuessLang(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeErr(w, http.StatusBadRequest, "invalid json body")
|
||||
return
|
||||
}
|
||||
lang := guessLang(req.Content)
|
||||
writeJSON(w, http.StatusOK, map[string]any{"language": lang})
|
||||
}
|
||||
Reference in New Issue
Block a user