text:
Suffix Alias Script

Hey, I was looking for a while if there is some way I can have a "suffix alias" which are available in unix shell like zsh? For example, we can set an alias like below:
alias -s git="git clone"

And then all of these commands:
https://github.com/chrisant996/clink-gizmos.git
git@github.com:chrisant996/clink-gizmos.git

Would become expand to these when we press enter:
git clone https://github.com/chrisant996/clink-gizmos.git
git@github.com:chrisant996/clink-gizmos.git

This is useful for other scenarios, for example I could type "image.png" into my terminal and it'll open it with my image editor instead of having to type like "paintdotnet image.png"
If there is some function in clink that allows me get the contents of the prompt and then substitute it with a different command before it will be executed that would be amazing!
(This is possible in powershell but I'm wondering if it's possible in cmd.exe with clink)

Windows has "File Associations", but I don't think they apply to URLs the way you want.
In a Lua script loaded into Clink, you can do any arbitrary preprocessing of the input line before it gets passed to CMD via clink.onfilterinput().

Windows has "File Associations". That's how e.g. image.png opens in whatever app is registered to handle the .PNG extension. Read about File Associations in Windows for more info.
But I don't think File Associations apply to URLs the way you want in the two URL examples you gave.
In a Lua script loaded into Clink, you can do any arbitrary preprocessing of the input line before it gets passed to CMD via clink.onfilterinput(). You could do your own processing to give the effect you're looking for.
But watch out for edge cases. E.g. foo.git should NOT expand if there's a file foo.git.exe along the system %PATH%. (Or foo.git.bat, or any other extension listed in %PATHEXT%.)

Thank you! 😃
It didn't turn out to be very complicated to make, which is nice
local suffix_aliases = {
	[".git"] = "git clone",
}

local function file_exists_in_directory(directory, filename)
	local separator = "\\"
	local path = directory .. separator .. filename
	local file = io.open(path, "r")
	if file then
		file:close()
		return true
	end
	return false
end

local function is_in_path(program)
	local path_env = os.getenv("PATH")
	local path_ext = os.getenv("PATHEXT")

	if not path_ext then
		error("%PATHEXT% is not available")
	elseif not path_env then
		error("%PATH% is not available")
	end

	local extensions = {}

	for str in string.gmatch(path_ext, "([^;]+)") do
		table.insert(extensions, str)
	end

	local split_path = string.gmatch(path_env, "[^;]+")

	for dir in split_path do
		for _, ext in ipairs(extensions) do
			if file_exists_in_directory(dir, program .. ext) then
				return true
			end
		end
	end

	return false
end

local function string_endswith(string, suffix)
	return string:sub(-#suffix) == suffix
end

local function onfilterinput(text)
	for suffix, command in pairs(suffix_aliases) do
		if string_endswith(text, suffix) then
			local text_without_suffix = string.sub(text, 0, #text - #suffix)
			if not is_in_path(text_without_suffix) then
				return command .. " " .. text
			end
		end
	end
end

if clink.onfilterinput then
	clink.onfilterinput(onfilterinput)
else
	clink.onendedit(onfilterinput)
end

I can now create as many suffixes as I want!
I would like the suffix to also be highlighted with clink.argmatcher() and i tried this:
local function onfilterinput(text)
	for suffix, command in pairs(suffix_aliases) do
		if string_endswith(text, suffix) then
+                       clink.argmatcher(text)
			local text_without_suffix = string.sub(text, 0, #text - #suffix)
			if not is_in_path(text_without_suffix) then
				return command .. " " .. text
			end
		end
	end
end

Since now the entire text is in argmatcher, I was expecting it to highlight everything with color.argmatcher but it didn't, do you have any advice?
Would you be interested in adding this to the repository?

I would like the suffix to also be highlighted with clink.argmatcher() when it's a valid suffix and i tried this:
local function onfilterinput(text)
	for suffix, command in pairs(suffix_aliases) do
		if string_endswith(text, suffix) then
+                       clink.argmatcher(text)
			local text_without_suffix = string.sub(text, 0, #text - #suffix)
			if not is_in_path(text_without_suffix) then
				return command .. " " .. text
			end
		end
	end
end

Since now the entire text is in argmatcher, I was expecting it to highlight everything with color.argmatcher but it didn't, do you have any advice?

There are some problems with that:

The onfilterinput function is called after you press Enter.  Nothing done in there could affect input line coloring before you press Enter.
Calling simply clink.argmatcher(text) creates a blank argmatcher that does nothing at all.
Creating a new argmatcher for each input line would consume a lot of memory over time.
The input line coloring is merely a small side effect of an argmatcher.  Argmatchers do tons more than that, but it looks like you want none of the actual behaviors or benefits of argmatchers.
Using color.argmatcher would be misleading since this has nothing to do with argmatchers and is incompatible with any of their behaviors.  Consider creating a new color setting instead (or maybe use color.doskey, but that may also be misleading).

If you want to apply custom coloring to the input line, then refer to Setting a classifier function for the whole input line.
Also, the text variable contains the entire command line.  The way the code is written, when text is echo foo bar address.git the code turns it into git clone echo foo bar address.git.  Was that intended?  It seems undesirable.

also how about adding this to the repository?

This isn't ready to be included in a repo:

To configure suffixes, one must edit the Lua script directly, which requires creating a fork of the repo and doing manual operations to keep the forked repo up to date with the main repo.  There would need to be a way to configure suffixes without modifying the script.
The echo foo bar address.git example might be an issue.
There isn't documentation.

Also, if it's included in the repo, then that implies I would take over maintenance of it.  I'm not sure I want to do that.
It seems like something that would be good to distribute separately, e.g. in separate repo that you maintain.

More thoughts:

If you hook up a clink.classifier() to do input line coloring, then there's an edge case to consider:  The call to is_in_path() is a blocking call and can take a while, depending on text (especially since it's looping over all suffixes).  Classifiers need to be very fast, otherwise they'll introduce lag while typing and/or updating the display.  To ensure high performance, it would need to use os.getdrivetype() and check for remote drives before calling file_exists_in_directory().

Using io.open() in file_exists_in_directory() is an inefficient way to check for file existence, especially if the file is on a remote drive.  Use os.isfile() instead.

Is looping over the list of suffixes necessary?  That will get slower the more suffixes are defined.  It's already set up as an associative table with suffixes as keys.  The loop could be eliminated by getting the suffix from the line and indexing into the table.
local function onfilterinput(text)
    local suffix = text:match("(%.[^. /\\]+)$")
    if suffix then
        local command = suffix_aliases[suffix]
        if command then
            return command .. " " .. text
        end
    end
end

[!WARNING]
The example above is not trying to fix the issue where echo foo bar repo.git become git clone echo foo bar repo.git.  Fixing that requires more sophisticate parsing of text.

Also, what about an input line echo foo & repo.git, or an input line repo.git & echo done?
You can use clink.parseline(text) to get a table of line_state objects for each of the commands in the text input line, and then apply suffix alias expansion to each command.

Also, what about an input line echo foo & repo.git, or an input line repo.git & echo done?
You can use clink.parseline(text) to get a table of line_state objects for each of the commands in the text input line, and then apply suffix alias expansion to each command.

Hey, thank you for the insightful responses! I updated the color matching to use a custom user config theme.
After thinking about it for a bit, I'm not sure if the added complexity and performance costs are really worth command like cd.git not being expanded.
Since I was basing this suggestion off of zsh, I think it makes sense to implement it how they exactly have it. This has the added benefit that people can transfer their ZSH suffix aliases directly into clink
So the way it works in zsh is that it will iterate through every single word in the input line and expand them individually. For example echo foo bar.git will become echo foo bar git clone bar.git, and zsh doesn't account if something can be an executable. For example cd.git will just expand to git clone cd.git
Here's the current code:
settings.add("color.suffix", "", "Color for when suffix is matched")

local suffixes = {
	[".git"] = "git clone",
}

local function escape_pattern(str)
	return str:gsub("(%W)", "%%%1")
end

local pattern_parts = {}

for suffix, _ in pairs(suffixes) do
	table.insert(pattern_parts, escape_pattern(suffix) .. "$")
end

local combined_pattern = table.concat(pattern_parts, "|")

local suffix_classifier = clink.classifier(1)
function suffix_classifier:classify(commands)
	local line = commands[1].line_state:getline()
	local color = settings.get("color.suffix") or ""
	local classifications = commands[1].classifications
	local last_index = 1

	for word in line:gmatch("%S+") do
		local match = word:match(combined_pattern)
		local start_index, end_index = string.find(line, word, last_index, true)
		last_index = end_index + 1

		if match and suffixes[match] then
			classifications:applycolor(start_index, end_index - start_index + 1, color)
		end
	end
end

local function onfilterinput(line)
	local words = {}
	for word in line:gmatch("%S+") do
		local match = word:match(combined_pattern)
		if match and suffixes[match] then
			word = suffixes[match] .. " " .. word
		end
		table.insert(words, word)
	end
	return table.concat(words, " ")
end

if clink.onfilterinput then
	clink.onfilterinput(onfilterinput)
else
	clink.onendedit(onfilterinput)
end

I wanted to also allow the user to manually specify suffix aliases. I wasn't sure how to do it the way I really envisioned how its gonna work:

I define a new setting suffix
Users can set whatever properties they want, like:

suffix..git = git clone
suffix.!random123 = foo

And then I can iterate through the "suffix" setting in my code and check each property.
But I don't think this is possible, so instead I tried the following: (I replace the commented out part with the non-commented out part.)
settings.add("suffix.aliases", ".js=nvim;.git=git clone;", "Suffix aliases")

local suffixes = {}

for suffix_prepend_pair in string.gmatch(settings.get("suffix.aliases"), "([^;]+)") do
	local suffix = {}
	for suffix_or_prepend in string.gmatch(suffix_prepend_pair, "([^=]+)") do
		table.insert(suffix, suffix_or_prepend)
	end

	suffixes[suffix[1]] = suffix[2]
end

-- local suffixes = {
-- 	[".git"] = "git clone",
--     [".js"] = "nvim",
-- }

For some reason, the script no longer works at all. I'm not sure why this is happening. I used serializeTable function i found from this stack overflow post so that I can debug the suffixes table. In both cases (with the commented out parts switched around) the suffixes table had exactly the same structure:
{
  .git = git clone,
  .js = nvim,
}

However with the new approach, where I add the suffix.aliases setting, it just stops working. The code is not asynchronous (I don't think...?) so this doesn't make sense to me. The structure of suffixes is identical, so why it suddenly stops working?
If you don't want to add it to the repo I can understand that. i can add documentation and publish a repo once I'm able to figure out how I can let users define their suffix alias in clink_settings
(by the way I tried implementing your idea where we look at each word and then check its ending. The problem with that is users can define any suffix alias, it doesn't have to start with a period)

So the way it works in zsh is that it will iterate through every single word in the input line and expand them individually. For example echo foo bar.git will become echo foo bar git clone bar.git, and zsh doesn't account if something can be an executable. For example cd.git will just expand to git clone cd.git

Ah, ok, that makes it simpler and more consistent.  This is sounding promising...

Here's the current code:

The zsh parity approach sounds interesting, and I'll load the script and share my observations after I've examined it a bit.

This has the added benefit that people can transfer their ZSH suffix aliases directly into clink
...
I wanted to also allow the user to manually specify suffix aliases. I wasn't sure how to do it the way I really envisioned how its gonna work:

How does one specify them in zsh?

Is there a file?  Why not have a way to read the file format?  Similar to how Clink uses Readline and therefore loads your existing .inputrc file, if you have one.
You could use clink.onfilterinput to implement a custom command like suffix-alias suffix=expansion, similar to how clink-flex-prompt implements a custom flexprompt configure command.

I define a new setting suffix

And then I can iterate through the "suffix" setting in my code and check each property.
But I don't think this is possible

I agree that using multiple settings wouldn't result in a nice user experience.  Trying to use multiple suffix.whatever settings would be problematic because settings in Clink are not meant to be dynamically created by users.  There's no way to dynamically delete a setting that's been created, for example.

so instead I tried the following: (I replace the commented out part with the non-commented out part.)
settings.add("suffix.aliases", ".js=nvim;.git=git clone;", "Suffix aliases")

local suffixes = {}

for suffix_prepend_pair in string.gmatch(settings.get("suffix.aliases"), "([^;]+)") do
	local suffix = {}
	for suffix_or_prepend in string.gmatch(suffix_prepend_pair, "([^=]+)") do
		table.insert(suffix, suffix_or_prepend)
	end

	suffixes[suffix[1]] = suffix[2]
end

-- local suffixes = {
-- 	[".git"] = "git clone",
--     [".js"] = "nvim",
-- }

For some reason, the script no longer works at all. I'm not sure why this is happening.

Re: "no longer works at all" -- But what does happen?  I can see a few ways to improve the code, but I don't see anything that would make it "no longer work at all".  I do see that if you try to use clink set suffix.aliases new_list_of_suffix_aliases then it won't update to use the new list, because the Lua script only parses the setting when the script is loaded, which only happens when starting Clink or when forcing a reload e.g. via Ctrl-X,Ctrl-R.
If you want to debug it, you could run clink set lua.debug true and then add a line pause() at the spot where you want to break into the debugger and start debugging.  Type help at the debugger prompt for available commands.

I used serializeTable function i found from this stack overflow post so that I can debug the suffixes table.

You could use dumpvar() which is part of clink-gizmos (and is probably richer than what's in the stackoverflow post you found).
In the Lua debugger, you could use dump suffixes to show the table contents, or you could use dumpvar(suffixes) (since you have clink-gizmos loaded already and the Lua debugger lets you run any arbitrary Lua code at the debugger prompt).

If you don't want to add it to the repo I can understand that. i can add documentation and publish a repo once I'm able to figure out how I can let users define their suffix alias in clink_settings

With the additional info and simplification to match how zsh works, it's something I might be willing to add into clink-gizmos.

(by the way I tried implementing your idea where we look at each word and then check its ending. The problem with that is users can define any suffix alias, it doesn't have to start with a period)

Got it.  Without optimizations, that will begin to perform poorly as the number of suffix definitions grows.  To optimize for that, I would probably use a data structure like a Trie to enable fast matching in reverse starting from the end of a string (e.g. implement a sparse Trie using Lua associative tables).

I found the bug.

settings.add("suffix.aliases", ".js=nvim;.git=git clone;", "Suffix aliases")

local suffixes = {}

for suffix_prepend_pair in string.gmatch(settings.get("suffix.aliases"), "([^;]+)") do
	local suffix = {}
	for suffix_or_prepend in string.gmatch(suffix_prepend_pair, "([^=]+)") do
		table.insert(suffix, suffix_or_prepend)
	end

	suffixes[suffix[1]] = suffix[2]
end

-- local suffixes = {
-- 	[".git"] = "git clone",
--     [".js"] = "nvim",
-- }

For some reason, the script no longer works at all. I'm not sure why this is happening. I used serializeTable function i found from this stack overflow post so that I can debug the suffixes table. In both cases (with the commented out parts switched around) the suffixes table had exactly the same structure:

The bug
It has nothing to do with the table structure.
If you restore the commented-out code, then it will also not work.
Any time there is more than one suffix defined, the code won't work.
That's the difference that made it stop working.
Because the code tries to construct a regular expression string instead of a Lua pattern string.
Lua patterns do not have a | operator.
Performance
But if Lua did have regular expressions, then the code would be constructing a regular expression whose size and complexity is proportional to the number of suffixes defined.  That could work (if Lua had regular expressions), but its performance wouldn't scale very well as the number of suffixes grows.
The performance almost might not matter much if it only occurs when pressing Enter.
But performance matters for input line coloring.
But, optimizations can come later
The first thing to do is to get the basics working, and configurability (e.g. reading a file of suffix alias definitions).
So for starters, looping over pairs(suffixes) would work and would be a good start.  Maybe not ready for including in clink-gizmos, but probably plenty good enough for your own purposes.

I found the bug.

settings.add("suffix.aliases", ".js=nvim;.git=git clone;", "Suffix aliases")

local suffixes = {}

for suffix_prepend_pair in string.gmatch(settings.get("suffix.aliases"), "([^;]+)") do
	local suffix = {}
	for suffix_or_prepend in string.gmatch(suffix_prepend_pair, "([^=]+)") do
		table.insert(suffix, suffix_or_prepend)
	end

	suffixes[suffix[1]] = suffix[2]
end

-- local suffixes = {
-- 	[".git"] = "git clone",
--     [".js"] = "nvim",
-- }

For some reason, the script no longer works at all. I'm not sure why this is happening. I used serializeTable function i found from this stack overflow post so that I can debug the suffixes table. In both cases (with the commented out parts switched around) the suffixes table had exactly the same structure:

The bug
It has nothing to do with the table structure.
If you restore the commented-out code, then it will also not work. Any time there is more than one suffix defined, the code won't work. That's the difference that made it stop working.
Because the code tries to construct a regular expression string instead of a Lua pattern string. Lua patterns do not have a | operator.
Performance
But if Lua did have regular expressions, then the code would be constructing a regular expression whose size and complexity is proportional to the number of suffixes defined. That could work (if Lua had regular expressions), but its performance wouldn't scale very well as the number of suffixes grows.
The performance almost might not matter much if it only occurs when pressing Enter. But performance matters for input line coloring.
But, optimizations can come later
The first thing to do is to get the basics working, and configurability (e.g. reading a file of suffix alias definitions).
So for starters, looping over pairs(suffixes) would work and would be a good start. Maybe not ready for including in clink-gizmos, but probably plenty good enough for your own purposes.

thank you! I ended up replacing it with this instead:
local function get_suffix(word)
	for suffix in pairs(suffixes) do
		local pattern = escape_pattern(suffix) .. "$"
		if word:match(pattern) then
			return suffix
		end
	end
	return false
end

Not sure if this will be faster than the loop method, but we'll see.
The way zsh allows for suffix aliases is very simple, it just adds a -s flag to the alias command. alias is similar to doskey
For example we can have a file, my-alias.sh
alias -s .git="git clone"

And then when I make it executable and run it by typing its name, I'll have the .git suffix alias available to use. I can also just directly type the command, and I can put the line in any of my scripts which execute on startup
So to allow user customizability right now we may have two options:

using a single clink setting and then parsing it (not best for user experience)
storing it in a file and then reading the file (but maybe reading a file can be slow?, and maybe someone with 1 suffix alias wouldn't want to have to store it in a file, so maybe we can provide both options?)

Current script:

settings.add("color.suffix", "", "Color for when suffix is matched")

local suffixes = {
	[".git"] = "git clone",
	[".js"] = "nvim",
}

local function escape_pattern(str)
	return str:gsub("(%W)", "%%%1")
end

local function get_suffix(word)
	for suffix in pairs(suffixes) do
		local pattern = escape_pattern(suffix) .. "$"
		if word:match(pattern) then
			return suffix
		end
	end
	return false
end

local suffix_classifier = clink.classifier(1)
function suffix_classifier:classify(commands)
	local line = commands[1].line_state:getline()
	local color = settings.get("color.suffix") or ""
	local classifications = commands[1].classifications
	local last_index = 1

	for word in line:gmatch("%S+") do
		local match = get_suffix(word)
		local start_index, end_index = string.find(line, word, last_index, true)
		last_index = end_index + 1

		if match and suffixes[match] then
			classifications:applycolor(start_index, end_index - start_index + 1, color)
		end
	end
end

local function onfilterinput(line)
	local words = {}
	for word in line:gmatch("%S+") do
		local match = get_suffix(word)
		if match and suffixes[match] then
			word = suffixes[match] .. " " .. word
		end
		table.insert(words, word)
	end
	return table.concat(words, " ")
end

if clink.onfilterinput then
	clink.onfilterinput(onfilterinput)
else
	clink.onendedit(onfilterinput)
end

As written, the onfilterinput function is replacing all multi-space sequences with a single space.  That makes it impossible to enter certain kinds of commands.  That's one of the reasons it can be very dangerous to do onfilterinput processing.
I have a version with several changes and bug fixes, which I'll share soon.

This fixes some subtle yet important issues, such as:

Didn't respect quotes.
Converted all runs of 2 or more spaces into a 1 space.
Didn't update when the suffix.aliases setting gets changed.

It also improves efficiency a little bit, but it doesn't implement a Trie yet.
I added some "FUTURE" comments for where/how further enhancements could be made.
It seems to work for me, but I haven't tested it extensively.

[!NOTE]
I added a DEBUG_SUFFIX_ALIASES environment variable which can be set to only print the expansion without actually returning it to CMD.  E.g. set DEBUG_SUFFIX_ALIASES=1.

if not clink.parseline then
    log.info("suffix_aliases.lua requires a newer version of Clink; please upgrade.")
    return
end

settings.add("color.suffix_alias", "bri mag", "Color when a suffix alias is matched")
settings.add("suffix.aliases", ".js=nvim;.git=git clone", "List of suffix aliases")

local suffixes = {}

--------------------------------------------------------------------------------
-- Ensure that suffixes are loaded.
--
-- For now this just loads from a setting, any time the setting changes.
--
-- Eventually it could reload from a file, for example whenever the file
-- timestamp changes.

local prev_suffixes

local function ensure_suffixes()
    -- FUTURE:  To load from a file, this could check the timestamp and compare
    -- it to a prev_timestamp value, and bail out if they match.
    local s = settings.get("suffix.aliases")
    if s == prev_suffixes then
        return
    end
    prev_suffixes = s

    -- FUTURE:  To load from a file, this could be replaced with f = io.open()
    -- and a for loop over f:lines().
    for _,entry in ipairs(string.explode(s, ";")) do
        local name,value = entry:match("^%s*([^=]-)%s*=%s*(.*)%s*$")
        if name then
            suffixes[name] = value
        elseif entry ~= "" and entry:gsub(" ", "") ~= "" then
            log.info(string.format("Unable to parse '%s' in setting suffix.aliases.", entry))
        end
    end

    -- FUTURE:  This is where to build a sparse Trie data structure (e.g. using
    -- Lua tables with string.byte() values as keys) that can optimize matching
    -- suffixes starting from the end of a word.  To reduce construction time
    -- and memory consumption, it could probably use a Trie for only the last 5
    -- to 10 characters, and then switch to looping over substrings.
    --
    -- E.g. this illustrates the a sparse Trie that switches to an array of
    -- substrings after the last 2 characters:
    --
    --  {
    --      [string.byte("t")] =
    --      {
    --          [string.byte("i")] =
    --          {
    --              [".g"] = "git clone",
    --          },
    --      },
    --      [string.byte("d")] =
    --      {
    --          [string.byte("l")] =
    --          {
    --              ["wor"] = "echo hello",
    --              ["fo"] = "echo poker",
    --          },
    --          [string.byte("e")] =
    --          {
    --              ["r"] = "echo color",
    --          },
    --      },
    --  }
end

clink.onbeginedit(ensure_suffixes)

--------------------------------------------------------------------------------
-- Look up a suffix alias for a word.

local function get_suffix_alias(word)
    local wordlen = #word
    if wordlen > 0 then
        -- FUTURE:  Performance scales poorly as the number of defined suffixes
        -- grows.  That could be improved with a sparse Trie data structure that
        -- matches characters in reverse order from the end of the word.
        for name,value in pairs(suffixes) do
            local namelen = #name
            if wordlen >= namelen and string.matchlen(word:sub(wordlen + 1 - namelen), name) == -1 then
                return value, name
            end
        end
    end
end

--------------------------------------------------------------------------------
-- Do input line coloring.

local suffix_classifier = clink.classifier(1)
function suffix_classifier:classify(commands)
    -- Do nothing if no color is set.
    local color = settings.get("color.suffix_alias") or ""
    if color == "" then
        return
    end

    -- Loop through the commands in the input line and apply coloring to each.
    for _,command in ipairs(commands) do
        local line_state = command.line_state
        local classifications = command.classifications
        for i = 1, line_state:getwordcount() do
            local word = line_state:getword(i)
            if get_suffix_alias(word) then
                local info = line_state:getwordinfo(i)
                classifications:applycolor(info.offset, info.length, color)
            end
        end
    end
end

--------------------------------------------------------------------------------
-- Provide an input hint (requires Clink v1.7.0 or newer).
-- The input hint is displayed below the input line, in the comment row (like
-- where history.show_preview displays history expansion previews).

local suffix_hinter = clink.hinter and clink.hinter(1) or {}
function suffix_hinter:gethint(line_state)
    local cursorpos = line_state:getcursor()
    for i = line_state:getwordcount(), 1, -1 do
        local info = line_state:getwordinfo(i)
        if cursorpos > info.offset + info.length then
            return
        elseif info.offset <= cursorpos and cursorpos <= info.offset + info.length then
            local word = line_state:getword(i)
            local value, suffix = get_suffix_alias(word)
            if value then
                local hint = string.format("%s=%s", suffix, value)
                if (settings.get("color.suffix_alias") or "") == "" then
                    hint = "Suffix alias "..hint
                end
                return hint, info.offset
            end
            return
        end
    end
end

--------------------------------------------------------------------------------
-- Insert suffix commands before any word that has a matching suffix alias.

local function onfilterinput(line)
    local last = 0
    local parts = {}
    local commands = clink.parseline(line)

    for _,command in ipairs(commands) do
        local line_state = command.line_state
        for i = 1, line_state:getwordcount() do
            local word = line_state:getword(i)
            local value = get_suffix_alias(word)
            if value then
                local info = line_state:getwordinfo(i)
                local next
                if info.quoted then
                    next = info.offset - 1
                else
                    next = info.offset
                end
                table.insert(parts, line:sub(last + 1, next - 1))
                table.insert(parts, value.." ")
                last = next - 1
            end
        end
    end

    if last > 0 then
        table.insert(parts, line:sub(last + 1, #line))

        local result = table.concat(parts, "")

        if os.getenv("DEBUG_SUFFIX_ALIASES") then
            print("SUFFIX ALIAS EXPANSION:")
            print(result)
        else
            line = result
        end
    end

    return line
end

clink.onfilterinput(onfilterinput)

This fixes some subtle yet important issues, such as:
* Didn't respect quotes.

* Converted all runs of 2 or more spaces into a 1 space.

* Didn't update when the suffix.aliases setting gets changed.

It also improves efficiency a little bit, but it doesn't implement a Trie yet. I added some "FUTURE" comments for where/how further enhancements could be made.
It seems to work for me, but I haven't tested it extensively.
Note
I added a DEBUG_SUFFIX_ALIASES environment variable which can be set to only print the expansion without actually returning it to CMD. E.g. set DEBUG_SUFFIX_ALIASES=1.
if not clink.parseline then
    log.info("suffix_aliases.lua requires a newer version of Clink; please upgrade.")
    return
end

settings.add("color.suffix_alias", "bri mag", "Color when a suffix alias is matched")
settings.add("suffix.aliases", ".js=nvim;.git=git clone", "List of suffix aliases")

local suffixes = {}

--------------------------------------------------------------------------------
-- Ensure that suffixes are loaded.
--
-- For now this just loads from a setting, any time the setting changes.
--
-- Eventually it could reload from a file, for example whenever the file
-- timestamp changes.

local prev_suffixes

local function ensure_suffixes()
    -- FUTURE:  To load from a file, this could check the timestamp and compare
    -- it to a prev_timestamp value, and bail out if they match.
    local s = settings.get("suffix.aliases")
    if s == prev_suffixes then
        return
    end
    prev_suffixes = s

    -- FUTURE:  To load from a file, this could be replaced with f = io.open()
    -- and a for loop over f:lines().
    for _,entry in ipairs(string.explode(s, ";")) do
        local name,value = entry:match("^%s*([^=]-)%s*=%s*(.*)%s*$")
        if name then
            suffixes[name] = value
        elseif entry ~= "" and entry:gsub(" ", "") ~= "" then
            log.info(string.format("Unable to parse '%s' in setting suffix.aliases.", entry))
        end
    end

    -- FUTURE:  This is where to build a sparse Trie data structure (e.g. using
    -- Lua tables with string.byte() values as keys) that can optimize matching
    -- suffixes starting from the end of a word.  To reduce construction time
    -- and memory consumption, it could probably use a Trie for only the last 5
    -- to 10 characters, and then switch to looping over substrings.
    --
    -- E.g. this illustrates the a sparse Trie that switches to an array of
    -- substrings after the last 2 characters:
    --
    --  {
    --      [string.byte("t")] =
    --      {
    --          [string.byte("i")] =
    --          {
    --              [".g"] = "git clone",
    --          },
    --      },
    --      [string.byte("d")] =
    --      {
    --          [string.byte("l")] =
    --          {
    --              ["wor"] = "echo hello",
    --              ["fo"] = "echo poker",
    --          },
    --          [string.byte("e")] =
    --          {
    --              ["r"] = "echo color",
    --          },
    --      },
    --  }
end

clink.onbeginedit(ensure_suffixes)

--------------------------------------------------------------------------------
-- Look up a suffix alias for a word.

local function get_suffix_alias(word)
    local wordlen = #word
    if wordlen > 0 then
        -- FUTURE:  Performance scales poorly as the number of defined suffixes
        -- grows.  That could be improved with a sparse Trie data structure that
        -- matches characters in reverse order from the end of the word.
        for name,value in pairs(suffixes) do
            local namelen = #name
            if wordlen >= namelen and string.matchlen(word:sub(wordlen + 1 - namelen), name) == -1 then
                return value, name
            end
        end
    end
end

--------------------------------------------------------------------------------
-- Do input line coloring.

local suffix_classifier = clink.classifier(1)
function suffix_classifier:classify(commands)
    -- Do nothing if no color is set.
    local color = settings.get("color.suffix_alias") or ""
    if color == "" then
        return
    end

    -- Loop through the commands in the input line and apply coloring to each.
    for _,command in ipairs(commands) do
        local line_state = command.line_state
        local classifications = command.classifications
        for i = 1, line_state:getwordcount() do
            local word = line_state:getword(i)
            if get_suffix_alias(word) then
                local info = line_state:getwordinfo(i)
                classifications:applycolor(info.offset, info.length, color)
            end
        end
    end
end

--------------------------------------------------------------------------------
-- Provide an input hint (requires Clink v1.7.0 or newer).
-- The input hint is displayed below the input line, in the comment row (like
-- where history.show_preview displays history expansion previews).

local suffix_hinter = clink.hinter and clink.hinter(1) or {}
function suffix_hinter:gethint(line_state)
    local cursorpos = line_state:getcursor()
    for i = line_state:getwordcount(), 1, -1 do
        local info = line_state:getwordinfo(i)
        if cursorpos > info.offset + info.length then
            return
        elseif info.offset <= cursorpos and cursorpos <= info.offset + info.length then
            local word = line_state:getword(i)
            local value, suffix = get_suffix_alias(word)
            if value then
                local hint = string.format("%s=%s", suffix, value)
                if (settings.get("color.suffix_alias") or "") == "" then
                    hint = "Suffix alias "..hint
                end
                return hint, info.offset
            end
            return
        end
    end
end

--------------------------------------------------------------------------------
-- Insert suffix commands before any word that has a matching suffix alias.

local function onfilterinput(line)
    local last = 0
    local parts = {}
    local commands = clink.parseline(line)

    for _,command in ipairs(commands) do
        local line_state = command.line_state
        for i = 1, line_state:getwordcount() do
            local word = line_state:getword(i)
            local value = get_suffix_alias(word)
            if value then
                local info = line_state:getwordinfo(i)
                local next
                if info.quoted then
                    next = info.offset - 1
                else
                    next = info.offset
                end
                table.insert(parts, line:sub(last + 1, next - 1))
                table.insert(parts, value.." ")
                last = next - 1
            end
        end
    end

    if parts[1] then
        table.insert(parts, line:sub(last + 1, #line))

        local result = table.concat(parts, "")

        if os.getenv("DEBUG_SUFFIX_ALIASES") then
            print("SUFFIX ALIAS EXPANSION:")
            print(result)
        else
            line = result
        end
    end

    return line
end

clink.onfilterinput(onfilterinput)

this is really awesome, thank you!

Just chiming in that fish's abbr does something similar - expand input as you type (i.e. it doesn't wait for the command to be executed).
Would it be hard to modify this to serve that purpose as well? Possibly toggling between behaviors with a clink setting?

Just chiming in that fish's abbr does something similar - expand input as you type (i.e. it doesn't wait for the command to be executed).
Would it be hard to modify this to serve that purpose as well? Possibly toggling between behaviors with a clink setting?

Kind of?
It would be possible to create something similar to fish's abbr.  But I wouldn't recommend using this script as a starting point for that -- there's very little similarity at an implementation level.
A script could use clink.onaftercommand() and look for available expansions for text preceding the cursor position.  But it would have to also check rl.getlastcommand() to test what command was used -- for example, a cursor navigation command like forward-word shouldn't trigger expansions.
I don't plan to work on something like this.  Also, I would personally not use something that automatically replaces what I type -- too often that kind of thing makes it impossible to type certain things.  I think a key binding to perform expansion on demand would fit my own usage style better.  But I don't plan to work on something like that, either.
(And FWIW, I can't use the suffix script because it makes it impossible to run certain kinds of commands, because it unconditionally expands things -- even the .git example for git clone made it impossible to run multiple commands I needed to run.  I don't entirely understand why some people seem to have no problems with suffix expansion, but I accept it as a truth, albeit a mysterious truth.)

Yeah, I understand. Filtering real-time vs one-shot would have different priorities.
On my usage of fish abbreviations:
I mostly use it as a better alias (which is a bash feature fish doesn't support), since I can see immediately what will be run - which matters to me but would not to everyone.
For example, abbreviating CUDA_VISIBLE_DEVICES= to cvd= - you can see why. Another one that I use very often is cact/mact being conda activate and mamba activate.
Usually these are abbreviations that will not get in the way of anything else. (If I didn't explain it before, fish won't just replace every occurrence - will only do when the whole word matches, and you press a space afterward. You can ofc undo it.)
So I've never faced inconveniences with abbreviations - but maybe that is because of the abbreviations I set.

If in fish it only does something in response to Space then simply do that same thing in Clink.

Make a Lua function that does expansions on the word before the cursor.
Bind it to the Space key.

See Lua key bindings.
The suffix alias script is unrelated.

Windows has doskey which is similar to bash's alias.
Enabling Showing Input Hints in Clink lets doskey_hinter.lua show a preview of how an alias will expand.

I've written a prototype script abbr.lua which is in this gist at the moment.  Eventually it may get added to clink-gizmos.

I knew about the alias hints - I have it enabled, but usually the hint pops up ~0.75s after typing (is this an artificial delay because of debouncing the input?) so I don't wait for it.
Nice prototype!
I liked that you added 2 undogroups as well. It works with the semi-minimal testing I've done, just needs some color.
I've been working on that, taking reference from the envvar_highlighter. Left a comment on the gist.

I knew about the alias hints - I have it enabled, but usually the hint pops up ~0.75s after typing (is this an artificial delay because of debouncing the input?) so I don't wait for it.

See comment_row.hint_delay.  Default is 500 ms.

Is there a reason for this delay? I couldn't find it in the doc

Is there a reason for this delay? I couldn't find it in the doc

Several reasons.  Try it without a delay, they'll probably become clear.  🙃

I don't notice any lag/flashing/weird behaviour 😅
Maybe my list of macros is very small:
la=ls -A $*
ll=ls -lAh $*
gitbash="C:\Program Files\Git\usr\bin\bash.exe" $*
gnutar="C:\Program Files\Git\usr\bin\tar.exe" $*
smerge="C:\Program Files\Sublime Merge"\smerge.exe $*

What was supposed to happen?

I imagine you are probably only looking at what happens with the abbr.lua hints, and not any of the (many) other things that provide hints.
When hints show up instantly, then there's flicker, and also it shifts the input line up a row if at the bottom of the screen.  And a couple other things that I forgot.
I originally had no delay, and quickly realized that was not acceptable at all, in the big picture.

These are doskeys macros, not abbrs
Weirdly, I don't see any flicker, at all - I've even recorded it here
_the second half is empty as i looked for how to exit the recording
I could even screen-record if you want, but this should be more accurate as it captures everything sent to the terminal.

These are doskeys macros, not abbrs
Weirdly, I don't see any flicker, at all - I've even recorded it here _the second half is empty as i looked for how to exit the recording
I could even screen-record if you want, but this should be more accurate as it captures everything sent to the terminal.

I think the reason you don't experience it as "flicker" is because you're typing very slowly.  It's impossible to have flicker when typing slowly.  Sorry for not explaining that; I assumed it was clear.

Oh, understood, you mean to say the flicker caused by actually continuing typing. That doesn't bother me too much since I know it is because of my own actions and not something undesirable, and also serves as another way to check in passing if I entered the right thing.
But I guess the setting makes sense, I'm keeping it at 0 though 😆.

But I guess the setting makes sense, I'm keeping it at 0 though 😆.

As you like.
One kind of situation where flicker got really annoying was like this:
g = one command
gc = a second command
gco = a third command
As you type g then c then o, it flickers between three different hints.  I have a bunch of cases like that, and I type quickly, which made immediate hints highly distracting.  (Plus there's the issue of forced scrolling when at the bottom of the screen.)

Yeah that makes sense

The abbr discussion was off-topic from this issue; I wish I had moved it to a separate issue for proper tracking.
In any case, the abbr.lua script has been added to the clink-gizmos repo.

Back on topic...
The suffix alias script is still not added to clink-gizmos.
Because even the original "git clone" example causes problems for me:  when a suffix alias is defined for ".git" then it becomes impossible to run a command like "dir .git" because it gets replaced.  This completely blocked me from doing multiple different kinds of operations when a suffix alias was defined.
Suffix aliases seem problematic to me, so I haven't invested time finishing the TODO items in the prototype script.


text:
vreduce cannot handle Vector input when using dims argument

julia> vreduce(+, [1,2,3], dims = 1)
ERROR: BoundsError: attempt to access 0-element Array{Symbol,1} at index [1]

This error affects calling softmax([0.5, 0.5].

I added a simple fix.
The underlying problem -- handling stored reductions that don't actually have any associated loops -- would be a little more complicated. So I'm adding this simple fix for now.

Thanks!

source: gharchive/issue

authors: AStupidBear chriselrod

license: MIT

license_type: permissive

license_source: github-api


text:
[ feature ] Ctrl+scroll and Ctrl+click as alternatives keybinds

Shift+scroll and Shift+click are working.
But it seems the Shift key is already being used by a custom node to do something else !
Can we have user settings to use Ctrl+scroll and Ctrl+click as alternatives ?

normal:

while the Shift is pressed:
( this does not happen in I disable all custom nodes )

As @LukeG89 mentioned, this is a frontend feature, that uses the Shift key
https://github.com/Comfy-Org/ComfyUI_frontend/pull/1157
So maybe we should have Ctrl+scroll and Ctrl+click as default, in the Controller.

Both click and scroll can now be set to use shift- or ctrl-

It's working.

repo: chrisgoringe/cg-controller


text:
UE? text boxes can appear in front of the edit box of other nodes

See https://github.com/chrisgoringe/cg-use-everywhere/issues/139#issuecomment-1998606086

OK, I think I'm on the test branch already.
This issue seems to be fixed.

Fix merged.

repo: chrisgoringe/cg-use-everywhere


text:
The workflow you've loaded may have connection/linking data that could be fixed. Open fixer  |  Fix in place

I get that every single time I load a workflow with this in it.  Any ideas?

This is another custom node plugin (pythonssss? Maybe?) - you can ignore it. I'll see if I can work it out with the other developer!

This is another custom node plugin (pythonssss? Maybe?) - you can ignore it. I'll see if I can work it out with the other developer!

Would be great and I love this as I have been shouting for a long time now that we needed nodes like this to get rid of the noodles only this one went one better than I was talking about.   Fantastic work with this one crazy other plugin being triggered.  pythonssss is too valuable to remove too.

Reopening this to prompt me to work out out with pythonsssss

My fingers are crossed.

Same here, but the nodes are amazing, especially as I look to increasingly make my workflow into modular templates! Too cool for words. Also, I can confirm it works with Efficiency Nodes:

Yep, all my workflows has this now, but it is so annoying that the popup never goes away on its own, I have to click it.  I hope they work it out.

thanks, rg. It would be a big help if anyone who has a workflow where this happens could post it here, so I can investigate whether there is a UE bug or a situation that could be handled better.

@rgthree - can you give me the tl;dr version of what you're checking for? UE nodes don't modify the underlying graph, but hijack the graph_to_prompt method to modify what gets sent in the prompt.

Totally. Also, note, most ppl have a ton of nodes. This may not actually be a UE issue, especially if it's not changing the graph data in the UI.
Okay, so when a workflow is loaded (manually, or dragging a JSON or Image into the window) it checks for the live app.graph for bad links. (If there was an error loading the workflow, then it will check the SerializedGraph directly from the workflow instead of the live ComfyUI graph. The code is messy to accommodate/normalize both graph data types.)
Anyway, the routine goes over the graph.links list, and verifies that the data in every defined link can be corroborated with the nodes it says it's linking. That is:

that the origin and target nodes for the list even exist in the graph nodes list
that the origin node's outputs[link.origin_slot].links has that link id in its list
that the target node's inputs[link.target_slot].link has that link id defined there, etc.

And than take a action from several possibilities depending on what's identified above.
The source is at [rgthree-comfy/blob/main/ts/link_fixer.ts](https://github.com/rgthree/rgthree-comfy/blob/65832bcc83880e8ad5c57cef04b4754673dba971/ts/link_fixer.ts#L38, though there is a lot happening in there, the start of the iteration would be further down

@rgthree With those revelations what is the everywhere node not doing that is failing those checks to make the pop up happen?

@rgthree - in link_fixer.js line 240 I think the log message is incorrect? origin_node and target_node used inconsistently?:
                logger.log(`${link.id} is def invalid; BOTH origin node ${link.origin_id} ${originNode ? "is removed" : `doesn\'t have ${link.id}`} and ${link.origin_id} target node ${link.target_id ? "is removed" : `doesn\'t have ${link.id}`}.`);

maybe should read
                logger.log(`${link.id} is def invalid; BOTH origin node ${link.origin_id} ${!originNode ? "is removed" : `doesn\'t have ${link.id}`} and ${link.target_id} target node ${!target_node ? "is removed" : `doesn\'t have ${link.id}`}.`);

AHA!
UE doesn't modify the graph, but what gets serialized when you save a workflow...? Looks like the workflow is being saved with UE pseudo-links included, instead of the graph being saved.
I've created a new issue #65 to track this, but I think it'll fix the problems here.

OK - this is fixed in 4.3.8.
new saved workflows will no longer have the UE links included, and should not trigger the warning.
existing workflows (saved with UE before 4.3.8) will still have them; you can use rgthree's auto cleanup to remove them, and then resave, and all should be well.
If you find this warning recurs after that, please reopen this issue (or open a new one).

Awesome! (And, @chrisgoringe , I think you're right about the message in link_fixer).

authors: DarkAlchy aegis72 chrisgoringe rgthree


text:
Uncaught TypeError: Cannot read property 'href' of undefined

[Enter steps to reproduce below:]

...
...

Atom Version: 1.7.3
System: Unknown Windows Version
Thrown From: autocomplete-angular-material package, v0.4.0
Stack Trace
Uncaught TypeError: Cannot read property 'href' of undefined
At /C:/Users/Abdel-Malik22/.atom/packages/autocomplete-angular-material/lib/provider.coffee:252

TypeError: Cannot read property 'href' of undefined
    at Object.module.exports.getAttributeValues (file:///C:/Users/Abdel-Malik22/.atom/packages/autocomplete-angular-material/lib/provider.coffee:172:41)
    at Object.module.exports.getAttributeValueCompletions (file:///C:/Users/Abdel-Malik22/.atom/packages/autocomplete-angular-material/lib/provider.coffee:131:15)
    at Object.module.exports.getSuggestions (file:///C:/Users/Abdel-Malik22/.atom/packages/autocomplete-angular-material/lib/provider.coffee:18:8)
    at C:\Users\Abdel-Malik22\AppData\Local\atom\app-1.7.3\resources\app.asar\node_modules\autocomplete-plus\lib\autocomplete-manager.js:310:56
    at Array.forEach (native)
    at AutocompleteManager.module.exports.AutocompleteManager.getSuggestionsFromProviders (C:\Users\Abdel-Malik22\AppData\Local\atom\app-1.7.3\resources\app.asar\node_modules\autocomplete-plus\lib\autocomplete-manager.js:289:17)
    at AutocompleteManager.getSuggestionsFromProviders (C:\Users\Abdel-Malik22\AppData\Local\atom\app-1.7.3\resources\app.asar\node_modules\autocomplete-plus\lib\autocomplete-manager.js:3:61)
    at AutocompleteManager.module.exports.AutocompleteManager.findSuggestions (C:\Users\Abdel-Malik22\AppData\Local\atom\app-1.7.3\resources\app.asar\node_modules\autocomplete-plus\lib\autocomplete-manager.js:276:19)
    at C:\Users\Abdel-Malik22\AppData\Local\atom\app-1.7.3\resources\app.asar\node_modules\autocomplete-plus\lib\autocomplete-manager.js:3:61

Commands
  2x -0:08.0 core:backspace (atom-text-editor.editor.is-focused.power-mode)

Config
{
  "core": {
    "autoHideMenuBar": true,
    "disabledPackages": [
      "auto-indent",
      "autocomplete-snippets",
      "autocomplete-html"
    ],
    "packagesWithKeymapsDisabled": [
      "css-clean"
    ],
    "themes": [
      "atom-material-ui",
      "atom-material-syntax"
    ]
  }
}

Installed Packages
# User
activate-power-mode, v0.5.2
atom-idiomatic-comments-css-snippets, v0.1.0
atom-material-syntax, v0.4.6
atom-material-ui, v1.2.11
auto-detect-indentation, v1.0.0
autocomplete-angular-material, v0.4.0
autocomplete-html-entities, v0.1.0
autocomplete-php, v0.3.7
color-picker, v2.1.1
emmet, v2.4.3
icon-fonts, v2.5.2
minimap, v4.23.5
minimap-find-and-replace, v4.5.1
seti-icons, v1.0.1
symfony-snippets, v0.3.0
tag, v0.4.0

# Dev
No dev packages

Duplicate Issue #3

authors: Abdel-MalikSimplon chrisgriffith

license: mit

license_source: bigquery


text:
no way to identify selected radio button with empty fields

When I use the mutually exclusive group with action='store', there appears to be no way to distinguish between "button was not selected" and "button was selected but field was empty".
See e.g. this example code, based on the example at https://github.com/chriskiehl/GooeyExamples/blob/master/examples/dynamic_updates.py :
import sys
from pprint import pprint

from gooey import Gooey, GooeyParser

@Gooey( program_name="Using Dynamic Values")
def main():
    parser = GooeyParser()

    g = parser.add_argument_group()

    stuff = g.add_mutually_exclusive_group(
        required=False,
        gooey_options={
            'initial_selection': 0
        }
    )
    stuff.add_argument(
        '--one',
        action='store',
    )
    stuff.add_argument(
        '--two',
        action='store'
    )

    args = parser.parse_args()
    pprint(args)

if __name__ == '__main__':
    print("-"*80)
    pprint(sys.argv)
    main()

If you run it with something filled in, all is as expected. However, if you run it with a radio button selected and the corresponding text field empty, that parameter doesn't get passed.
In other words, there is no way to distinguish between these 3 situations :

Output of the 3 runs :

I would have expected to see it being passed as e.g. an empty string. If I want to run this command manually, I could run it with e.g. --one '' or something, and see the difference that way.
Seen in gooey 1.0.3 and 1.0.4.
Currently on python 3.6.4 on MacOS.
I've also seen it on windows, on some python 2.7.

I'd argue that Gooey is still technically doing the Right Thing. The overall behavior of Gooey is that empty == nothing passed on the command line. Radio Group is following that behavior.
Gooey can't treat empty inputs as empty strings and pass them along, as that would break what it means for things to be optional from a CLI perspective (which is to say optional == not present in the argument list).
I do see this as a place where Gooey should be more helpful in guiding the user behavior. If they've selected a radio option, then they must provide a value. In effect, this means forcing Gooey to run a validator regardless of the state of the argument's required state.

I've tagged this for inclusion in 1.0.5.

@chriskiehl I see your point.
On the other hand, in some situations, choosing a radio button and passing an empty string is acceptable behaviour. E.g. my situation (described in #594 ) of saving a filter, where an empty filter is also valid.
Of course, I could work around that by adding a third option to the radio group, "delete filter", but that feels a bit clunky to me.
Would it be an option to have the behaviour you describe be the default, but add some option that allows empty strings to be passed?
With that option enabled, the behaviour could be :

radio button not selected -> None (as it is now)
radio button selected, empty field -> ''
obviously, radio button selected, something filled in, that gets passed.

Then again, if you're going to have some sort of searching/filtering in dropdowns, my whole reason for needing the radio button goes away, so I'd have no problem with it if you ignore this suggestion :-)

authors: chriskiehl frederikvs

repo: chriskiehl/Gooey


text:
Hide Runnning and Complete Screens

Hi there,
When I get first screen, I give the path to my document.
After that it shows Running screen and when the script done it will show Complete screen.
Is there anyway to hide Running and Complete screens?
Thank you in advance.

What do you have in mind for the behavior of the start button under this setup?

I found the solution, thanks.

authors: OrkhanHI chriskiehl


text:
windows app report error

when I was running the master in my win10 os.
weed.exe master

I1003 19:03:13 24244 master_grpc_server.go:72] added volume server 169.254.185.33:8080
W1003 19:03:13 24244 master_grpc_server.go:56] SendHeartbeat.Recv server 169.254.185.33:8080 : rpc error: code = Canceled desc = context canceled
I1003 19:03:13 24244 node.go:294] topo:DefaultDataCenter:DefaultRack removes 169.254.185.33:8080
I1003 19:03:13 24244 master_grpc_server.go:28] unregister disconnected volume server 169.254.185.33:8080
I1003 19:03:14 24244 masterclient.go:78] master masterClient failed to keep connected to 169.254.185.33:9333: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 169.254.185.33:19333: connectex: A socket operation was attempted to an unreachable network."
I1003 19:03:14 24244 masterclient.go:121] master masterClient failed to connect with master 169.254.185.33:9333: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 169.254.185.33:19333: connectex: A socket operation was attempted to an unreachable network."
I1003 19:03:15 24244 masterclient.go:78] master masterClient failed to keep connected to 169.254.185.33:9333: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 169.254.185.33:19333: connectex: A socket operation was attempted to an unreachable network."`

there was some network problem with connecting to 169.254.185.33:19333

authors: chrislusf seamusic

repo: chrislusf/seaweedfs


text:
Object.values is not a function

node version: v6.10.0
TypeError: Object.values is not a function
    at Object.isMobilePhone (/Users/kk/workspace/zan/node_modules/validator/lib/isMobilePhone.js:77:21)
    at Validator.isMobilePhone (/Users/kk/workspace/zan/lib/base/Validator.js:158:34)
    at Object.<anonymous> (/Users/kk/workspace/zan/test/base/validator.js:25:10)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)

Looks like a regression in 7.2.0. @chriso I guess this should be patched for 7.2 and merged back into master?

+1

authors: PauloLira hkwu kk0829


text:
More validators

Added postal code validator (fixes #682)
Added lat long pair validator (fixes #664)

Everything is up to spec. Referenced Unicode CLDR for postal code standards. I made a huge simplification of the recommend Great Britain Regex as it was too big for a lightweight library like this, but it works just fine.
Original GB regex (not js regex engine):
GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}

Let me know if I need to change anything!
Thanks,
Charles

great job!

Oops... I suppose that whitespace was randomly added in the build step. Fixed.

Thanks for the PR :smile:

Perfect, thanks.

@Charliekenney23 I had in mind the [Robustness Principle]( be liberal in what you accept from others) "be liberal in what you accept from others"
While I don't recommend creating coordinate strings with leading and trailing space or with a space before the comma, I very well expect that I might run into such data that has been hand-typed in a spreadsheet by a non-technical user that gets uploaded to my server.
It seems like a simple addition to make the library more robust. Even if these extra spaces are present, there's no doubt that the string would be intended to a be coordinate string.
As I understand, the goal is not to parse a "coordinate string standard", but rather to validate input "in the wild" that may be provided by humans or other computers that is expected to some amount of tolerable imperfections.

@markstos I totally understand what you're getting at here. Though, the same could be said for every validator in this repository that evaluates single line input data (e.g. email, phone number, ip address; which are also not preceded or appended with a none-or-more whitespace match). The norm would be to simply call the trim method on the string of subject before you even evaluate the string, then you know your data is normalized from the start.
As this library aims to provide convenience, it would make since to side with the norm of strict validation. Beyond that, it seems sloppy and redundant to add '\s*' to the start and end of every expression in this library. This should be left up to the user IMO.

@Charliekenney23 I get what you saying about not re-inventing trim().
I still think it would be helpful to tolerate the "space before comma" case, which is something that comes up from time-to-time in hand-entered CSV data.

source: gharchive/pull-request

authors: Charliekenney23 chriso landsman markstos


text:
2019-10-10 DNC 3.0.0 release

Update for DNC 3.0.0
I made the ServerSideBlazor project compile but could not get the database setup properly in order to test. CSB Blazor runs as expected (without the glitch in the context that I observed in the other CSB project).

I restructured the SSB project to be integral to the same server project (2nd commit)
I messed up with Git so the 3rd & 4th commits cancel each other out
I then changed the names of 3 of the 4 csproj files to not emphasize ClientSideBlazor since the app now supports both CSB & SSB (5th commit)

Here is a summary of the changes:

"Mechanical" changes to move from DNC preview version to release - These were essetially identical to the changes in the role based auth repo. (1st commit)
I wasn't a fan of the duplicate code in client side blazor library and the server side web project so I did the following refactoring:
2a) Separated the GUI code that was repeated in the ServerSideBlazor project by first making a copy of ClientSideBlazor.Client and calling it ClientSideBlazor.Blazor.
2b) Removed all GUI code from ClientSideBlazor.Blazor, leaving only Program.cs & Startup.cs
2c) Removed Program.cs, Startup.cs, and Blazor references in the csproj from ClientSideBlazor.Client thus converting it to a razor class library.
2d) Modified Startup.cs in ClientSideBlazor.Server to have some condition compilation based upon the configuration to either host the WASM based ClientSideBlazor.Blazor as CSB or to use ClientSideBlazor.Client as SSB.
2e) Removed the now redundant ServerSideBlazor project. (All of this is in the 2nd commit)
Ignore the 3rd & 4th commits, they cancel each other out and essentially really were Git issue related.
Finally, realizing that having ClientSideBlazor.xxx projects made no sense when they could be running either CSB or SSB I renamed all of the projects to be BlazorBasedAuth.xxx (5th commit)

Here is a summary of the changes:
"Mechanical" changes to move from DNC preview version to release - These were essetially identical to the changes in the role based auth repo. (1st commit)
I wasn't a fan of the duplicate code in client side blazor library and the server side web project so I did the following refactoring:
2a) Separated the GUI code that was repeated in the ServerSideBlazor project by first making a copy of ClientSideBlazor.Client and calling it ClientSideBlazor.Blazor.
2b) Removed all GUI code from ClientSideBlazor.Blazor, leaving only Program.cs & Startup.cs. Startup.cs references the app.razor assembly in ClientSideBlazor.Client.
2c) Removed Program.cs, Startup.cs, and Blazor references in the csproj from ClientSideBlazor.Client thus converting it to a razor class library.
2d) Modified Startup.cs in ClientSideBlazor.Server to have some condition compilation based upon the configuration to either host the WASM based ClientSideBlazor.Blazor as CSB or to use ClientSideBlazor.Client as SSB.
2e) Removed the now redundant ServerSideBlazor project. (All of this is in the 2nd commit)
Ignore the 3rd & 4th commits, they cancel each other out and essentially really were Git issue related.
Realizing that having ClientSideBlazor.xxx projects made no sense when they could be running either CSB or SSB I renamed all of the projects to be BlazorBasedAuth.xxx (5th & 6th commits).
I found a bug in the SSB implementation that kept the FetchData page from working when there was an 'autologon' from a saved token in local storage. Basically the base address of the http client needed to be set in app.razor rather than AuthService.cs (which, in retrospect, was a poor choice on my part). Anyhow 7th commit.

Hi Mark, so sorry it's taken me this long to get to these PRs.
I'll be honest, I'm not sure about the changes you've made. This repo is about providing an easy to follow code sample that mimics the blog post. Code efficiency isn't the goal here - it's allowing people to understand as easily as possible.
I don't think I can accept this PR, I'm really sorry.

authors: MarkStega chrissainty

repo: chrissainty/PolicyBasedAuthWithBlazor


text:
Downgrade Microsoft.ApplicationInsights.AspNetCore minimum version to 2.12.0

Related to this issue

Thanks :-)


text:
Component.isFormDisabled throws error

It happens on rerender form on component change. You can reproduce it by changing Form disabled prop in runtime(on onSubmit for example).
Uncaught TypeError: undefined is not a function Mixin.js?d263:110 module.exports.isFormDisabled

Hi @mloginov ,
Found the bug, will be fixed on next version, thanks for reporting! :-)

I'm using preact+bun+vite so perhaps it's one of their faults (I didn't go into it) but I think you have to know about it:

import React from 'react';
import { Form, Input } from 'formsy-react-components';

const RequestModal = () => {
    function submitForm(data) {
        console.log(data)
    }
    return (

Header

            <Form onSubmit={submitForm} validateBeforeSubmit={true} >
                <Input
                    name={'firstname'}
                    label={'Name'}
                />
            </Form>

    );
};

export default RequestModal;

authors: christianalfoni mloginov smbrine

repo: christianalfoni/formsy-react