Support for svn status in prompt #2582

Closed
wants to merge 5 commits into
from

Projects

None yet

2 participants

@samdmarshall
Contributor

This is to add support for displaying the output of svn status in a prompt similar to the way git and hg status get displayed. I tried to match the styling and conventions as best as I could, please let me know if there is any glaring issues with it.

Note: I wrote this against fish 2.2.0, so it doesn't take advantage of any of the new string manipulation features as there hasn't been published release that includes support for that yet. I would be happy to update this once a new release is made.

@faho faho and 1 other commented on an outdated diff Dec 9, 2015
share/functions/__fish_svn_prompt.fish
@@ -0,0 +1,169 @@
+# colour of the revision number to display in the prompt
+set -g __fish_svn_prompt_color_revision yellow
+
+# setting the prompt status separator character
+set -g __fish_svn_prompt_char_separator "|"
+
+# ==============================
+
+# SVN status symbols
+# Do not change these! These are the expected characters that are output from `svn status`, they are taken from `svn help status`
@faho
faho Dec 9, 2015 Member

If these aren't supposed to be changed, why are they variables?

@samdmarshall
samdmarshall Dec 9, 2015 Contributor

It seemed like this was the most efficient way to access them for parsing, I don't like they are global defines but there didn't seem to be a more efficient way of doing this when the helper function they get used in will be called many times.

@faho
faho Dec 10, 2015 Member

How about either adding them as local variables to the function they are used in or doing a switch/case there? Or make a list of these characters, another of the names and then map them via index:

set __fish_svn_status_chars A C D I M N R # ...
set __fish_svn_statuses added conflicted deleted ignored modified replaced # ...
echo $__fish_svn_prompt_char_{$__fish_svn_statuses(contains -i -- $status $__fish_svn_status_chars)}

(untested, the parser might barf on that)

@faho
faho Dec 10, 2015 Member

I don't think setting them as local variables is going to take that much time. In my experience most fishscript is taking the most time in calls to external tools (the hg_prompt was particularly bad about that, to the point that we've added custom code to find the .hg directory). Even though svn seems reasonably quick, it's still a few forks.

My gut tells me that optimization here is achieved by replacing grep or math (which forks to bc), not via defining variables.

If you wish to measure, fish has a nice "-p" option to output profiling data (though do keep in mind that even non-interactive fish will still read all config files).

@samdmarshall
samdmarshall Dec 10, 2015 Contributor

Since this gets called 7x in a pretty tight loop i was worried that a local variable setup/teardown would cause a bit of unwanted overhead to an already expensive function call. If you think that is fine, I can make that change but I am concerned about the performance of doing this.

Edit: Ok then I'll give that a try and update the PR.

@faho faho and 1 other commented on an outdated diff Dec 9, 2015
share/functions/__fish_svn_prompt.fish
+
+set -g __fish_svn_prompt_char_token_stolen_display 'T'
+set -g __fish_svn_prompt_char_token_stolen_color --bold purple
+
+set -g __fish_svn_prompt_char_token_broken_display 'B'
+set -g __fish_svn_prompt_char_token_broken_color --bold magenta
+
+
+# ==============================
+
+# this sets up an array of all the types of status codes that could be returned.
+set -g __fish_svn_prompt_flag_names added conflicted deleted ignored modified replaced unversioned_external unversioned missing locked scheduled switched token_present token_other token_stolen token_broken
+
+function __fish_svn_prompt_parse_status --argument flag_status_string --description "helper function that does pretty formatting on svn status"
+ # iterate over the different status types
+ for flag_index in (seq (count $__fish_svn_prompt_flag_names))
@faho
faho Dec 9, 2015 Member

Why not just iterate over __fish_svn_prompt_flag_names directly? The index is only used to access that.

@samdmarshall
samdmarshall Dec 9, 2015 Contributor

It was giving me some trouble with multiple substitution in variable names. Might have been my error, so I will have another look at it.

@faho
Member
faho commented Dec 9, 2015

The sed and grep calls could be replaced with string (not in a release yet, unfortunately).

@faho faho and 1 other commented on an outdated diff Dec 9, 2015
share/functions/__fish_svn_prompt.fish
+ # 2. remove extra lines that aren't necessary
+ # 3. cut the output down to the first 7 columns, as these contain the information needed
+ # 4. replaces newline characters with ":" to work around multi-line string handling
+ set -l svn_status_lines (command svn status | sed -e 's=^Summary of conflicts.*==' -e 's=^ Text conflicts.*==' -e 's=^ Property conflicts.*==' -e 's=^ Tree conflicts.*==' -e 's=.*incoming .* upon update.*==' | cut -c 1-7 | tr '\n' ':')
+
+ # track the last column to contain a status flag
+ set -l last_column 0
+
+ # iterate over the 7 columns of output (the 7 columns are defined on `svn help status`)
+ for col in (seq 7)
+ # get the output for a particular column
+ # 1. echo the whole status flag text
+ # 2. swap the ":" back to newline characters so we can perform a column based `cut`
+ # 3. cut out the current column of characters
+ # 4. remove spaces and newline characters
+ set -l column_status (echo $svn_status_lines | tr ':' '\n' | cut -c $col | tr -d ' \n')
@faho
faho Dec 9, 2015 Member

Use printf "%s\n" $svn_status_lines and you can nix replacing the newlines.

What happens is that fish will split command substitutions on newlines, making every line one argument, which then leads to svn_status_lines becoming a list with each line as one element. When you then hand it to echo, it'll read the arguments and echo them out separated by spaces. printf won't do that, instead using the format string once per argument.

@samdmarshall
samdmarshall Dec 9, 2015 Contributor

Interesting. OK i will look at changing that. I remember reading there was some funky-ness with handling multi-line strings in a variable in fish shell (and experiencing some of that myself). And came to this code as it "worked". Thanks!

@faho faho self-assigned this Dec 9, 2015
@faho
Member
faho commented Dec 9, 2015

I'll have to test this now. Any nice svn repo to try?

@samdmarshall
Contributor

Heh, the only public one that comes to mind is the llvm repos. This seems most work out of the svn repos I use at work.

@faho faho and 1 other commented on an outdated diff Dec 9, 2015
share/functions/__fish_svn_prompt.fish
+
+
+function __fish_svn_prompt --description "Prompt function for svn"
+ # if svn isn't installed then don't do anything
+ if not command -s svn > /dev/null
+ return 1
+ end
+
+ # make sure that this is a svn repo
+ set -l checkout_info (command svn info ^/dev/null)
+ if [ $status -ne 0 ];
+ return
+ end
+
+ # get the current revision number
+ set -l repo_revision_number (command svn info | grep "Last Changed Rev: " | sed "s=Last Changed Rev: ==")
@faho
faho Dec 9, 2015 Member

Hurray for localization - this won't work if your language isn't english. A simple workaround is of course to set -lx LC_ALL C set -lx LANGUAGE en (it seems svn will read that, ignoring existing conventions) here, though I'd rather have a less hackish solution.

@faho
faho Dec 9, 2015 Member

Would svnversion work here?

@samdmarshall
samdmarshall Dec 9, 2015 Contributor

I did notice there seems to be a function in fish shell for fetching the revision number of a repo but I didn't use it because it also spits out some other strings and I wasn't sure if it was half-implemented or not.

As for the grep strings, I was basing this off the source of the svn command printing out what appear to be english strings.

@faho
faho Dec 9, 2015 Member

I did notice there seems to be a function in fish shell for fetching the revision number

__fish_print_svn_rev? That has the same issue as it does basically the same thing.

As for the grep strings, I was basing this off the source of the svn command printing out what appear to be english strings.

They are english strings if your locale is english. Mine is german, so it'll print german strings (e.g. "Letzte geänderte Rev").

I'm trying to find a portable way to get svn information (i.e. the svn counterpart to git rev-parse or hg status), but I'm not coming up with much. svnversion might fit the bill, but I'm not sure if it prints everything you want here, svnlook is another, but it works only with repositories, not working copies.

@samdmarshall
samdmarshall Dec 9, 2015 Contributor

Yes, I will look at using svnversion here and see if it satisfied the needs. Maybe the function should be refactored to use svnversion then I should call that instead of implementing it twice.

it looks like this might work for using svnversion:

svnversion | sed -e 's=[^0-9:]*==g' -e 's=^.*:==' but i'd have to test it out a bit first.

Edit: Now realizing that the second -e in the sed might not be necessary and be removing desired data to display to the user. I'm ok with removing that second sed expression, if you think that would be locale agnostic.

@faho
faho Dec 9, 2015 Member

I'm ok with removing that second sed expression, if you think that would be locale agnostic.

I'm not sure why it wouldn't be - svnversion for me outputs "255122M", though that's on a fresh checkout of llvm (I don't use svn). It doesn't seem to distinguish between "object modified" and "object missing" (ie. deleted) like svnstatus does, though, and that's localized as well.

If you'd like to use string, it'd look like svnversion | string replace -r '([0-9]*).*' '$1'.

Edit: Another way to get the revision seems to be svn info --show-item last-changed-revision, which seems to purely output the number.

@samdmarshall
samdmarshall Dec 9, 2015 Contributor

I would like to use string however since I don't use a development build of fish shell i wanted to avoid maintaining this twice until a formal release is made.

As for the localized status of svn status the code I looked at from the build of svn apple ships has hardcoded text strings in english for the bits of the output i am attempting to remove. I recognize that isn't sufficient for the numerous platforms that fish shell supports -- I don't know how to approach the problem to tackle this in a more agnostic manner.

@faho
faho Dec 10, 2015 Member

As for the localized status of svn status the code I looked at from the build of svn apple ships has hardcoded text strings in english for the bits of the output i am attempting to remove.

I haven't actually gotten any localized output from svn status - maybe that's not a thing anymore? (It looks like "! CODE_OWNERS.TXT" because I've removed that file to test)

I'm not quite sure how to cause a conflict to happen which might trigger some actual language.

Did you see the suggestion about svn info --show-item last-changed-revision? (There's also the option of getting xml output, but parsing that in shell is not an enviable task)

@samdmarshall
samdmarshall Dec 10, 2015 Contributor

I did see the suggestion, however that doesn't work for my version of svn, (doesn't recognize the --show-item flag). I think I'm going to go with the svnversion | sed -e 's=[^0-9:]*==g' command and try to get this PR updated today to reflect the changes we have discussed.

@faho faho referenced this pull request Dec 9, 2015
Closed

VCS-agnostic prompts #2592

@samdmarshall
Contributor

OK I've updated with changes we discussed, how does it look now?

@faho

As I said - this tr dance isn't necessary.

@samdmarshall
Contributor

Sorry about that, when i tried it out again I think i forgot to add the \n into the printf formatter not realizing that would make a significant difference in output handling. That should be fixed now.

@faho
Member
faho commented Dec 10, 2015

The svn status thing is still not perfect, but given that I haven't been able to trigger it and that I don't know of any better way to do it, it's probably okay.

I'll test the new version a bit more and if it passes I'll merge.

@samdmarshall
Contributor

OK, once a release of fish shell is made with the new string stuff I'll submit another PR converting the code over to using that where it can. That might give a bit more control around the filtering the additional lines out of svn status, I couldn't work out a simple way of doing it without introducing a lot of overhead with a set of awk commands or similar.

@faho
Member
faho commented Dec 10, 2015

That last commit changed the time from 255357 to 258585 (fish -p profile -c "__fish_svn_prompt")- in other words barely, if at all, measurable.

I've managed to get a 20% improvement, though, by removing the `math´ call.

@faho faho commented on the diff Dec 10, 2015
share/functions/__fish_svn_prompt.fish
+ # get the output for a particular column
+ # 1. echo the whole status flag text
+ # 2. cut out the current column of characters
+ # 3. remove spaces and newline characters
+ set -l column_status (printf '%s\n' $svn_status_lines | cut -c $col | tr -d ' \n')
+
+ # check that the character count is not zero (this would indicate that there are status flags in this column)
+ if [ (count $column_status) -ne 0 ];
+ # we only want to display unique status flags (eg: if there are 5 modified files, the prompt should only show the modified status once)
+ set -l column_unique_status (echo $column_status | sort | uniq)
+ # parse the status flags for this column and create the formatting by calling out to the helper function
+ set -l svn_status_flags (__fish_svn_prompt_parse_status $column_unique_status)
+
+ # the default separator is empty
+ set -l prompt_separator ""
+ for index in (seq (math "$col - $last_column"))
@faho
faho Dec 10, 2015 Member

Since you're not using $index inside the loop, you can just do seq $last_column $col. Or am I missing anything?

@samdmarshall
samdmarshall Dec 10, 2015 Contributor

it looks like it iterates an extra time when doing seq $last_column $col vs seq (math "$col - $last_column")

@faho
Member
faho commented Dec 10, 2015

I've merged this now since it's nice code that works and the time is dominated by the svn calls (I can't seem to replicate the math thing anymore). I've squashed it to f5dfebb.

Thanks for the nice work and quick response!

@faho faho closed this Dec 10, 2015
@samdmarshall samdmarshall deleted the samdmarshall:sdm-svn-prompt branch Dec 10, 2015
@faho faho added the releasenotes label Dec 11, 2015
@faho faho added this to the next-2.x milestone Dec 11, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment