mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-28 20:39:40 -08:00
feat: cache compiled regex
use mustcompile for regex. Always use mustcompile which throws a panic if the regex cannot be compiled. We call recover to detect the panic and display an error message to the user.
This commit is contained in:
parent
ee4b039e7a
commit
890d0ad0e1
|
@ -4,7 +4,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -48,10 +47,8 @@ func getWindowTitle(imageName, windowTitleRegex string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the first window of the first pid
|
// returns the first window of the first pid
|
||||||
_, windowTitle, err := GetWindowTitle(processPid[0], windowTitleRegex)
|
_, windowTitle := GetWindowTitle(processPid[0], windowTitleRegex)
|
||||||
if err != nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
return windowTitle, nil
|
return windowTitle, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,13 +144,10 @@ func GetWindowText(hwnd syscall.Handle, str *uint16, maxCount int32) (length int
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWindowTitle searches for a window attached to the pid
|
// GetWindowTitle searches for a window attached to the pid
|
||||||
func GetWindowTitle(pid int, windowTitleRegex string) (syscall.Handle, string, error) {
|
func GetWindowTitle(pid int, windowTitleRegex string) (syscall.Handle, string) {
|
||||||
var hwnd syscall.Handle
|
var hwnd syscall.Handle
|
||||||
var title string
|
var title string
|
||||||
compiledRegex, err := regexp.Compile(windowTitleRegex)
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", fmt.Errorf("Error while compiling the regex '%s'", windowTitleRegex)
|
|
||||||
}
|
|
||||||
// callback fro EnumWindows
|
// callback fro EnumWindows
|
||||||
cb := syscall.NewCallback(func(h syscall.Handle, p uintptr) uintptr {
|
cb := syscall.NewCallback(func(h syscall.Handle, p uintptr) uintptr {
|
||||||
var prcsID int = 0
|
var prcsID int = 0
|
||||||
|
@ -168,7 +162,7 @@ func GetWindowTitle(pid int, windowTitleRegex string) (syscall.Handle, string, e
|
||||||
return 1 // continue enumeration
|
return 1 // continue enumeration
|
||||||
}
|
}
|
||||||
title = syscall.UTF16ToString(b)
|
title = syscall.UTF16ToString(b)
|
||||||
if compiledRegex.MatchString(title) {
|
if matchString(windowTitleRegex, title) {
|
||||||
// will cause EnumWindows to return 0 (error)
|
// will cause EnumWindows to return 0 (error)
|
||||||
// but we don't want to enumerate all windows since we got what we want
|
// but we don't want to enumerate all windows since we got what we want
|
||||||
hwnd = h
|
hwnd = h
|
||||||
|
@ -183,5 +177,5 @@ func GetWindowTitle(pid int, windowTitleRegex string) (syscall.Handle, string, e
|
||||||
// it returns 0(error occurred) instead of 1(success)
|
// it returns 0(error occurred) instead of 1(success)
|
||||||
// In our case, title will equal "" or the title of the window anyway
|
// In our case, title will equal "" or the title of the window anyway
|
||||||
_ = EnumWindows(cb, 0)
|
_ = EnumWindows(cb, 0)
|
||||||
return hwnd, title, nil
|
return hwnd, title
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ function global:Set-PoshGitStatus {
|
||||||
$config = $global:PoshSettings.Theme
|
$config = $global:PoshSettings.Theme
|
||||||
$cleanPWD = $PWD.ProviderPath.TrimEnd("\")
|
$cleanPWD = $PWD.ProviderPath.TrimEnd("\")
|
||||||
$cleanPSWD = $PWD.ToString().TrimEnd("\")
|
$cleanPSWD = $PWD.ToString().TrimEnd("\")
|
||||||
$standardOut = @(&$omp --error="$errorCode" --pwd="$cleanPWD" --pswd="$cleanPSWD" --execution-time="$executionTime" --config="$config")
|
$standardOut = @(&$omp --error="$errorCode" --pwd="$cleanPWD" --pswd="$cleanPSWD" --execution-time="$executionTime" --config="$config" 2>&1)
|
||||||
# Restore initial encoding
|
# Restore initial encoding
|
||||||
[Console]::OutputEncoding = $originalOutputEncoding
|
[Console]::OutputEncoding = $originalOutputEncoding
|
||||||
# the ouput can be multiline, joining these ensures proper rendering by adding line breaks with `n
|
# the ouput can be multiline, joining these ensures proper rendering by adding line breaks with `n
|
||||||
|
|
40
src/regex.go
40
src/regex.go
|
@ -1,9 +1,36 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "regexp"
|
import (
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
regexCache map[string]*regexp.Regexp = make(map[string]*regexp.Regexp)
|
||||||
|
regexCacheLock = sync.Mutex{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getCompiledRegex(pattern string) *regexp.Regexp {
|
||||||
|
// try in cache first
|
||||||
|
re := regexCache[pattern]
|
||||||
|
if re != nil {
|
||||||
|
return re
|
||||||
|
}
|
||||||
|
|
||||||
|
// should we panic or return the error?
|
||||||
|
re = regexp.MustCompile(pattern)
|
||||||
|
|
||||||
|
// lock for concurrent access and save the compiled expression in cache
|
||||||
|
regexCacheLock.Lock()
|
||||||
|
regexCache[pattern] = re
|
||||||
|
regexCacheLock.Unlock()
|
||||||
|
|
||||||
|
return re
|
||||||
|
}
|
||||||
|
|
||||||
func findNamedRegexMatch(pattern, text string) map[string]string {
|
func findNamedRegexMatch(pattern, text string) map[string]string {
|
||||||
re := regexp.MustCompile(pattern)
|
// error ignored because mustCompile will cause a panic
|
||||||
|
re := getCompiledRegex(pattern)
|
||||||
match := re.FindStringSubmatch(text)
|
match := re.FindStringSubmatch(text)
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
if len(match) == 0 {
|
if len(match) == 0 {
|
||||||
|
@ -19,7 +46,7 @@ func findNamedRegexMatch(pattern, text string) map[string]string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func findAllNamedRegexMatch(pattern, text string) []map[string]string {
|
func findAllNamedRegexMatch(pattern, text string) []map[string]string {
|
||||||
re := regexp.MustCompile(pattern)
|
re := getCompiledRegex(pattern)
|
||||||
match := re.FindAllStringSubmatch(text, -1)
|
match := re.FindAllStringSubmatch(text, -1)
|
||||||
var results []map[string]string
|
var results []map[string]string
|
||||||
if len(match) == 0 {
|
if len(match) == 0 {
|
||||||
|
@ -40,6 +67,11 @@ func findAllNamedRegexMatch(pattern, text string) []map[string]string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func replaceAllString(pattern, text, replaceText string) string {
|
func replaceAllString(pattern, text, replaceText string) string {
|
||||||
re := regexp.MustCompile(pattern)
|
re := getCompiledRegex(pattern)
|
||||||
return re.ReplaceAllString(text, replaceText)
|
return re.ReplaceAllString(text, replaceText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func matchString(pattern, text string) bool {
|
||||||
|
re := getCompiledRegex(pattern)
|
||||||
|
return re.MatchString(text)
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Segment represent a single segment and it's configuration
|
// Segment represent a single segment and it's configuration
|
||||||
|
@ -112,10 +111,8 @@ func (segment *Segment) shouldIgnoreFolder(cwd string) bool {
|
||||||
list := parseStringArray(value)
|
list := parseStringArray(value)
|
||||||
for _, element := range list {
|
for _, element := range list {
|
||||||
pattern := fmt.Sprintf("^%s$", element)
|
pattern := fmt.Sprintf("^%s$", element)
|
||||||
matched, err := regexp.MatchString(pattern, cwd)
|
matched := matchString(pattern, cwd)
|
||||||
if err == nil && matched {
|
return matched
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -163,6 +160,17 @@ func (segment *Segment) mapSegmentWithWriter(env environmentInfo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (segment *Segment) setStringValue(env environmentInfo, cwd string) {
|
func (segment *Segment) setStringValue(env environmentInfo, cwd string) {
|
||||||
|
defer func() {
|
||||||
|
err := recover()
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// display a message explaining omp failed(with the err)
|
||||||
|
message := fmt.Sprintf("oh-my-posh fatal error rendering %s segment:%s", segment.Type, err)
|
||||||
|
fmt.Println(message)
|
||||||
|
segment.stringValue = "error"
|
||||||
|
segment.active = true
|
||||||
|
}()
|
||||||
err := segment.mapSegmentWithWriter(env)
|
err := segment.mapSegmentWithWriter(env)
|
||||||
if err != nil || segment.shouldIgnoreFolder(cwd) {
|
if err != nil || segment.shouldIgnoreFolder(cwd) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -84,8 +84,14 @@ func TestShouldIgnoreFolderRegexInverted(t *testing.T) {
|
||||||
IgnoreFolders: []string{"(?!Projects[\\/]).*"},
|
IgnoreFolders: []string{"(?!Projects[\\/]).*"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
got := segment.shouldIgnoreFolder(cwd)
|
// detect panic(thrown by MustCompile)
|
||||||
assert.False(t, got)
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
// display a message explaining omp failed(with the err)
|
||||||
|
assert.Equal(t, "regexp: Compile(`^(?!Projects[\\/]).*$`): error parsing regexp: invalid or unsupported Perl syntax: `(?!`", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
segment.shouldIgnoreFolder(cwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestShouldIgnoreFolderRegexInvertedNonEscaped(t *testing.T) {
|
func TestShouldIgnoreFolderRegexInvertedNonEscaped(t *testing.T) {
|
||||||
|
@ -94,6 +100,12 @@ func TestShouldIgnoreFolderRegexInvertedNonEscaped(t *testing.T) {
|
||||||
IgnoreFolders: []string{"(?!Projects/).*"},
|
IgnoreFolders: []string{"(?!Projects/).*"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
got := segment.shouldIgnoreFolder(cwd)
|
// detect panic(thrown by MustCompile)
|
||||||
assert.False(t, got)
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
// display a message explaining omp failed(with the err)
|
||||||
|
assert.Equal(t, "regexp: Compile(`^(?!Projects/).*$`): error parsing regexp: invalid or unsupported Perl syntax: `(?!`", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
segment.shouldIgnoreFolder(cwd)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue