Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

History cleanups #3267

Merged
merged 13 commits into from
Dec 2, 2017
Merged

History cleanups #3267

merged 13 commits into from
Dec 2, 2017

Conversation

adcxyz
Copy link
Contributor

@adcxyz adcxyz commented Oct 31, 2017

This PR cleans up a number of little problems in History and HistoryGui.
Because History is quite independent of the rest of SC,
I think this could still be included in 3.9 without any risk.

History:

  • moved gui code to gui folder for use on headless systems
  • added method evaluateLineAt
  • added support for record/playback of CmdPeriod
  • constrained textfile saving to scd and txt
  • updated and improved help file
  • removed various cruft

HistoryGui

  • removed a Font dependency, which complained on linux and windows
  • remove unneeded debug posts
  • reinstated resize button
  • renamed method from postInlined to showLineAt
  • removed cruft

@adcxyz adcxyz requested review from nhthn and telephon October 31, 2017 17:04
@mossheim
Copy link
Contributor

I really think we ought to restrict updates to 3.9 to fixes from the beta. It's a slippery slope that we can afford not to walk on for the next few weeks IMO. Something gets merged that has accidental bugs, we have to wait for a patch, etc. What do you think @snappizz ?

Copy link
Member

@telephon telephon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have preferred to have this in 3.9.0, but I agree that it could be done now, too, if we are not overly strict. The fixes were really needed.

@adcxyz
Copy link
Contributor Author

adcxyz commented Oct 31, 2017

Either is fine, and I don't want to be a pest - sorry for getting around to it so late !
Really needed would be removing the font dependency, and removing the debug posts.
But then, that is much of the code changes;
better helpfiles are always nice to have,
and the only new feature is small and simple (rec/play of CmdPeriod).
That is why I thought that maybe it is OK to take the whole thing.

@nhthn
Copy link
Contributor

nhthn commented Oct 31, 2017

mixed feelings on this one. i don't see the harm in this particular case since History is not a critical SC feature, but we don't want any more of these between now and 3.9.0.

@telephon
Copy link
Member

yes, agreed: trick or treat!

Copy link
Contributor

@mossheim mossheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've added a lot of material to this, thank you! I'm wondering if we could make the style less informal, because in its current form the terse, capitalization-free style seems a little unfriendly to newbies.

Having explicit descriptions of the return types/argument types could go a long way. Most of the time, the general function is clear from the name, but the type is not. For example, the lines argument of History.new.

I think this could definitely go into 3.9.0, I agree that none of this is a dangerous change, but IMO it still needs a fair bit of work on the documentation side.

@@ -5,11 +5,66 @@ categories:: Streams-Patterns-Events

description::

History keeps track of all code lines that are being executed, in order to forward them to other players, to easily reuse earlier versions, or to store and reproduce a performance. Since it records everything that is interpreted, there is only one privileged instance of History - code::History.current::.
(adc 2006/7)
History stores all code strings as they are being evaluated, in order to reuse code written earlier, to forward code to other players, or to store, reproduce, edit and analyse live-coded performances. It records every evaluated code string into a privileged instance of History - code::History.current::.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we replace usage of "privileged" with "singleton"? it's a more accurate term and will make the pattern usage apparent to people who know it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, done


method:: keepsLog
subsection:: configuring what to record and post
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this be shortened to "configuration"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, to match style, the first word of each subsection should be capitalized

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

remove last line from history

subsection:: repeating history
method::evaluateLineAt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is missing a description

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

add an entry to (current) history by hand.

method::drop
drop the newest n lines from history. if n is negative, drop the oldest n lines.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name in the description should match the name of the argument in the method, which is num.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


subsection::More internal methods:
method::new
create a new instance.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does the lines arg to new do? that needs to be documented

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

method::setDocStr
set string of current doc
method::rip
create a new doc with currently selected line
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would really appreciate it if you could put some newlines between methods. it's hard to read in the current state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, done

@@ -167,25 +147,20 @@ HistoryGui : JITGui {
.action_({ |lview|
var index = lview.value;
if (lview.items.isEmpty) {
"no entries yet.".postln;
// "no entries yet.".postln;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mixed whitespace after this comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually the commented-out post can just be removed, so I did that.

//.path_(docTitle); // don't lose title.
}

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"// result: ".postln;
result.postln;
} {
"// History code evaluation FAILED for line: ".postln;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think you need to capitalize "failed" here. also, it might be a good idea to post the error message in addition to the line that failed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scenario is that you play back a history, possibly in edited form, so the current state is not exactly the same, and some lines may fail.
Then I want to know when a line went wrong, and which one, so this should warn visibly.
So I would prefer changing this to:
warn("History code evaluation failed for line: ...")

As there may be multiple lines that fail, they should not spam the post window with long error posts every 2 seconds. Maybe we want a flag whether to post errors on playback?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there may be multiple lines that fail, they should not spam the post window with long error posts every 2 seconds.

Wouldn't it be a good idea to stop at the first line that fails? And you can just take the error message, not the whole stack trace. Also, isn't this method also putting out quite a lot of extra information itself anyway? (4 lines per line evaluated)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, seems we need two error modes for two use cases of History.play:

  1. historian mode - reconstruct History to replay as accurately as possible
    -> on error, stop and throw, so one can edit history to fix it
  2. showtime mode - replay history in segments, maybe in modified orders
    -> on error, keep playing, just post short info - next line might work again.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused, sorry... I'm just talking about evaluateAtLine, not play. Are they related in some way? It doesn't seem that History.play would ever result in calling this method.

But, I agree that's a good distinction to make. Would you rather do it as two functions or one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, you are right,
History.play does it differently, and should actually also protect against errors.
evaluateLineAt gets used from HistoryGui, and there it is not guaranteed that every line will work - state may not be as needed for the line, esp. if the line is code shared from other players by network.
ok, will consider what is best here.

method::filteredShorts
the filtered short lines for gui display
method:.setKeyFilter
set the key(s) to filter for, and filter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mean "and then perform the filter"? i think that would be hard to understand for a non-native English speaker

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it does, added

Copy link
Contributor

@mossheim mossheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for the quick edits :) much appreciated

@mossheim mossheim added the comp: sclang sclang C++ implementation (primitives, etc.). for changes to class lib use "comp: class library" label Nov 2, 2017
@nhthn nhthn added this to the 3.9 milestone Nov 4, 2017
@adcxyz
Copy link
Contributor Author

adcxyz commented Nov 8, 2017

factored evaluateLineAt into .eval which allows throwing errors or not,
and added a global ignoreErrors flag for convenient switching between
historical and experimental history playback modes.
I hope I did not miss any comments?
If not, then this is good to go now AFAICT.

@adcxyz
Copy link
Contributor Author

adcxyz commented Nov 9, 2017

@brianlheim
Wouldn't it be a good idea to stop at the first line that fails? And you can just take the error message, not the whole stack trace. Also, isn't this method also putting out quite a lot of extra information itself anyway? (4 lines per line evaluated)

thanks for these comments, they helped clear up the ideas for this:
History.eval is a history specific mode for code evaluation that may ignore errors or not,
and users have simple ways to adapt that behavior to their needs.

@adcxyz
Copy link
Contributor Author

adcxyz commented Nov 23, 2017

ping @brianlheim - did I catch all of your requested changes?

@@ -5,6 +5,7 @@ History { // adc 2006, Birmingham; rewrite 2007.

classvar <>saveFolder = "~/Desktop/", <logFolder, <logFile, <logPath, <>keepsLog = true;
classvar <>current, <>maxShortLength=65;
classvar <>ignoreErrors = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a code smell IMO. but if there's no way to just pass it directly to a method call, I guess it works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally it would be, yes :-) specifically here, it server two purposes:
when recording code experiments, you always write code that does not compile
or stops with errors, forgotten semicolons, etc etc.
all these broken lines would clutter recorded history, so it is useful to have th option to just forget them.
when performing with history playback, such as (First Rule of the Avantgarde:) Try it Backwards. in that case, any lines that depend on historically previous ones will produce errors.

a = [1, 2, 3]; // line 1
a.scramble; // line 2
// playing back line2 before line1 will produce 30 lines of Error post:
-> ERROR: Message 'scramble' not understood.
RECEIVER:
   nil
ARGS:
CALL STACK:
	DoesNotUnderstandError:reportError
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw
		arg this = <instance of DoesNotUnderstandError>
	Object:doesNotUnderstand
		arg this = nil
		arg selector = 'scramble'
		arg args = [*0]
	Interpreter:interpretPrintCmdLine
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "a.scramble"
		var doc = nil
		var ideClass = <instance of Meta_ScIDE>
	Process:interpretPrintCmdLine
		arg this = <instance of Main>
^^ The preceding error dump is for ERROR: Message 'scramble' not understood.
RECEIVER: nil

having your post window flooded with error messages in a show is annoying, and can slow down the IDE responses, so you want the option to suppress error posting - if you ever try turning it off, you will know exactly why you want it on ;-)
makes sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, not sure if that answers your comment - did it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my question was more about it having class scope as opposed to function scope, but I get what you're saying.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recording functions of History (and the related variables) are all class methods (class variables), because there is only one instance of Interpreter which we can add it to. I suppose instances could be useful for other kinds of logs, but this is not what the class is intended for currently.

The playback functions of History are all instance methods, because there are multple recordings that could be played back. So in such a way, it assumes that history happens only once, but leaves many traces.

I suspect that for clarity these could be two classes, or the recorder could have a single instance it redirects to. But for now, this looks good and has always worked rather well.

@@ -325,6 +335,34 @@ History { // adc 2006, Birmingham; rewrite 2007.
^str.clumps(indices.differentiate)
}

*evaluateLineAt { |index| current.evaluateLineAt(index) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to abbreviate 'eval' below, you should do it here too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -151,6 +158,10 @@ History { // adc 2006, Birmingham; rewrite 2007.

play { |start=0, end, verbose=true| // line numbers;
// starting from past 0 may not work.
if (lines.isEmpty) {
"History.current is empty, cannot play.".postln;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't strictly true, right? not sure why this check was added in preference to just doing a no-op.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, when users play a history (current or recorded) and nothing happens, they may be surprised. Next thing they would check is probably whether this history is empty - so why not tell them? History is used in live coding performances, and anything that helps and speeds up orientation live is desirable I think.
Maybe the line should be "History:play - the history is empty, so there is nothing play yet.".postln;
?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed it be more accurate, informative, and less worry-inducing:

		if (lines.isEmpty) {
			"% - % is empty, so there is nothing to play.".postf(
				thisMethod,
				if (this == History.current) { "History.current" } { "this history" }
			);

var docTitle = title ++ Date.getDate.format("%Y-%m-%d-%Hh%M-History");
Document.new(docTitle, this.storyString)
// path not working yet
//.path_(docTitle); // don't lose title.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these comments stil relevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess yes - Document:path_ still gives a NotYetImplemented error. would be noce to be able to set a path where this document would be saved when you hit save, rather than having to navigate and type in the path.
hm, could write it as a file first, and then read in the file...
I'll think about that some more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried again, setting path still does not work,
and IDE save dialog does not take the document title...
so I would keep the comment like this:
//.path_(docTitle); // path not working yet in Qt
// - would be nice to set and show a path
// where the doc can be saved later, by hand or automatically.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks! in the future, would be good to mark that with TODO or FIXME so it can be easily found in the codebase

HistoryGui.methods.detect { |m| m.name == \showLineAt }
);
this.showLineAt(index);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move this method to the deprecated folder

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@mossheim
Copy link
Contributor

@adcxyz a few more questions/thoughts, but other than that looks good. Thanks for all the work on this, and sorry it took me awhile to get to it!

@adcxyz
Copy link
Contributor Author

adcxyz commented Nov 25, 2017

@brianlheim - done.
thanks for your precision with all these details, they are all good improvements!

@adcxyz
Copy link
Contributor Author

adcxyz commented Nov 25, 2017

... and committed

@mossheim
Copy link
Contributor

@adcxyz

For future reference - "address all requested changes from review" and "make changes as requested by X" are not very helpful commit messages. It would be better in the future if you broke those up into more logical units - in this case, "History: deprecate postInlined", "History: rename evaluateLineAt -> evalLineAt", "History: update comment", and so on. It really does make a huge difference when using git blame or (god forbid) when attempting to revert a broken commit. If you don't have a good workflow yet for making commits, spending some time to streamline that is a good idea, because from my experience if that takes any more time than it needs to, it's very tempting to just take a commit-and-forget approach. Having convenient shortcuts for git add -p and git commit -m ".." (ga and gc respectively) has been revolutionary for me.

Copy link
Contributor

@mossheim mossheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to (reluctantly) say I am ok with this being in the next beta. There are some things here that seem dangerous but the documentation is very good and I trust that enough people use History that any bugs will be spotted relatively quickly.

@mossheim
Copy link
Contributor

Thanks @adcxyz !

@adcxyz
Copy link
Contributor Author

adcxyz commented Nov 27, 2017

For future reference - "address all requested changes from review" and "make changes as requested by X" are not very helpful commit messages. It would be better in the future if you broke those up into more logical units - in this case, "History: deprecate postInlined", "History: rename evaluateLineAt -> evalLineAt", "History: update comment", and so on.

yes, right, will pay more attention to that.
for this one, should I reset to before this batch commit, and redo them in 3 or 4 little steps?
or amend the commit message with 4 lines of comments?

@mossheim
Copy link
Contributor

it's up to you really. i don't mind if you leave it as-is--that's why i said "in the future". :)

@adcxyz
Copy link
Contributor Author

adcxyz commented Nov 28, 2017

ok - I tried to get this with amend, but seems a mess,
so lets just leave it as is. thanks @brianlheim !

@mossheim
Copy link
Contributor

git commit --amend, git push -f. you will need to force push because the last commit's hash has changed. (for future reference.)

@mossheim mossheim merged commit 31ddaa3 into supercollider:3.9 Dec 2, 2017
@adcxyz adcxyz deleted the historyCleanups branch January 24, 2018 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp: sclang sclang C++ implementation (primitives, etc.). for changes to class lib use "comp: class library"
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants