« first day (1283 days earlier)      last day (1897 days later) » 

12:01 AM
RELOAD!
[retailcoder/Rubberduck] 55 commits. 12962 additions. 1725 deletions.
[rubberduck-vba/Rubberduck] 5 commits. 3 opened issues. 2 issue comments. 321 additions. 122 deletions.
[Zomis/AdventOfCode2017] 4 commits. 147 additions. 17 deletions.
 
12:17 AM
Sub test()

    Dim tl As ITradeList
    Dim trades As Variant

    Set tl = ProxyFactory.TradeSheet(Sheet1)
    trades = tl.trades
    Debug.Print trades(2, 1)

    Set tl = ProxyFactory.TradeSheet(Sheet2)
    trades = tl.trades
    Debug.Print trades(2, 1)

End Sub
@Mat'sMug ^^ There's an example of a ProxyFactory, for situations in which you have multiple sheets that all adhere to the requirements of the interface.
For example, a workbook that contains separate order sheets for different state-codes, or situations in which you have separate workbooks for different state-codes. You can have a single proxy class that can work with multiple sheets, even if they're not in the same workbook.
 
 
1 hour later…
1:38 AM
nice =)
 
Simply invaluable if you’re a VBA developer. https://twitter.com/rubberduckvba/status/939584327708545024
 
 
2 hours later…
4:01 AM
@ThunderFrame Wondering - would the factory do validation? If so, how?
 
@this could do... check for range dimensions, headers, data validation, customProperties.... Depends upon how much validation you want done.
 
Actually validating them all would be quite tedious, I imagine. I can see using CustomProperties as a handy shortcut to guard against harebrained assignments.
 
 
9 hours later…
12:48 PM
guys, I've been having a nightmare on git
with the Retailcoder.VBE/RubberduckUI.resx
git instists, whatever I do, that changes have been made to this file
I cannot force a checkout or a merge, nor a push or a pull, until I "commit or discard the changes"
but whether I commit or reset head hard, absolutely nothing changes, git insists again that this file has changes
 
1:44 PM
jesus, I just deleted my whole Rubberduck folder and git-cloned again, what a nightmare
@Hosch250 I like how that sounds. Why don't we update it altogether? :)
 
2:26 PM
hey all, long time no see
saw this and thought of you all
now that 2.0 is out, I gotta fire up rubberduck and submit some more PRs for parsing errors - I know I have more
what with my extensive library of terrible VBA written over the last 10 years and all, haha
 
@NelsonVides do you happen to have 2 RubberduckUI.resx, contained in two "different" folders which has the same path?
@MitropoulosMitropoulos Great! the more bugs we can knock out, the better. :)
 
@this I have no idea of that :/
 
last week or so, I did have a weird situation where I had two RubberduckUI.resx and git thought I edited them both
git somehow interpretered the path Rubberduck\VBE as a single folder, not two folders down
so I had Rubberduck\VBE\RubberduckUI.resx and Rubberduck\VBE\RubberduckUI.resx getting checked in.
 
3:15 PM
@Mat'sMug / @M.Doerner I'm thinking that I need a rewriter that can be used without tied up to an existing module. Is that possible?
 
@this ..how? why?
 
The reasoning is that the rewriter is gotten at a module level... which brings lot of other baggage and it has no way to narrow the rewriting to only one procedure. I can't tell the rewriter (in its present form) to get me only text for only one procedure.
 
what are you rewriting that's not in a module?
 
The only thing I can see is the TokenStream which is awkward to tranverse
this is for the preview code
in the Extract Method
 
3:18 PM
I think I can just do replaces & inserts and not apply Rewrite() which will actually applies the changes
 
Indeed
 
but hte problem is that it's at the whole module, so I'm being forced to deal with all other details about where the procedure is, etc.
and to figure out where the selection for the changed text would go
because if I insert stuff, selection has to change
that's too much details to juggle.
 
So you want a TokenStreamRewriter without a TokenStream?
 
fake a blank module would suffice
 
That we can do
 
3:20 PM
do I actually pull from RubberduckTests for a fake module? I hope not
also that module can't be saved to the VBA project.
at least, it shouldn't.
 
You'll have to bring the mockers into the project, you can't reference RDTests
 
That's what I thought.
now I realize I'd have to mock the vBa project, too...
feels like I'm just trading in 200 lbs of baggage for 90.7185 kg of baggage....
2
can't a TokenStream simply be new'd?
 
You get the tokens from the lexer stream
you don't need to mock up the whole vbe, you just need a way to get a token stream / rewriter for some arbitrary string
Without affecting global parser state
 
yeah
 
Looks like a job for RD.Parsing
 
3:30 PM
The thing is that the exisitng mockup might not fit the bill if I understood the code
from what I see, they are simply building the mocks but doing nothing WRT parsing
since they use the RubberduckStateParser to do the parsing of the mocked components
 
Hang on... why do you need a "new TokenStream"??
could you not send the relevant tokens into a separate stream and aggregate them into a string?
that would allow you to sidestep the issue, wouldn't it?
 
separate stream from where?
 
I'm thinking 3.0 could definitely use the ability to get a TSR for an arbitrary string though
 
seems essential if you going to have preview in other places, too.
 
Yup
 
3:34 PM
s/stream/channel/
 
but it's encapsulated ATM.
I don't have a direct access to the channels
and I don't think I can make up new channels
 
@Vogel612 once we know exactly what scope is being edited, there's no reason to parse the entire module anymore
 
Mat, remind me again why we can't parse at procedure level right now?
 
Because we treat token position as in-editor positions
 
ah yes
insert one line in procedure, all other proces below get bumped
 
3:36 PM
Yup
Hmm well we'll still need to parse everything.. once
 
Oh yes, no way around that but if we can make it more granular, the subsequent parsing will be faster still
because most of time when we are writing code, it's within a procedure scope, not module scope
the exception is when you're monkeying in the declarations scope
 
Even then, we can just replace the whole declarations section in one go
Anyway so yeah we need to be able to give the parser a string and get a TSR for it
Preview boxes were simpler when rewriting was stupid string manipulations =)
 
That's what I almost did initially but now that Hosch made my life complicated by selecting the Dim statement....
and the more I thought about it, I decided I really needed a rewriter so that I can handle the selection with Dim statements in the middle
otherwise, I will get weird results where they wanna the variable a be a ByRef Parameter... ooops! there's a Dim a As String in the middle! Tee-hee!
Anyway....
Trying to follow how it's done - ITokenStream is something implemented by ANTLR
so I don't think I should try to implement it but rather have it get parsed (even if it's an empty stream)
and unless @Vogel612 can come with better way, I might be looking at adapting the FinishedParseComponentTask in the ParserRunnerBase.cs
 
4:11 PM
I think this is what I ultimately need....
                var tokenStreamProvider = new SimpleVBAModuleTokenStreamProvider();
                var tokens = tokenStreamProvider.Tokens(SelectedCode);
                var rewriter = new TokenStreamRewriter(tokens);
 
4:25 PM
I can try to upset that a bit...
 
if there's a better way, i'm all for it. I just want to do it right
 
I'm pretty sure that we could expose the Parser API somewhat differently
 
and expose the channel?
 
that would be an option ...
 
it might be my poor understanding but when I looked at channel before it seems it's write-once
and can't be subsequently changed
 
4:33 PM
sure. but IIUC you want to have procedure-level parsing of your extracted method, right?
 
well yeah - this is going to go into the preview code section.
I need to be able to move in/out Dim statements with ease
especially if they had selected some Dim statement in the middle of the selection.
then chose that variable to be a ByRef Parameter. In that case, I must delete that Dim statement
but if it's a Local Variable, then it must stay where it is (even if it's in middle)
(normally for other variables where the their Dim statements are not included and they are declared to be Local Variable, I will add them at the top of procedure. (I realize there's issue w/ Move Closer to Usage but it's sufficient for simplicity's sake)
This need not be the final selection that the user will make, though. They could make some changes, then only the final selection will be then written into the module, and has to be written differently because in the preview, I show the fields outside the procedure but in the module, they have to actually go into the end of the Declarations section, add the new method to the bottom of the module, then modify the original procedure.
 
4:49 PM
Cos pics = 1000 words....
As you see, the default was ByRef parameter, but Dim a As String was contained in the selection. Therefore it has to be deleted or we get some weird mojo (if not compiler errors)
This will generate valid code but it won't work the way you expect to...
and i haven't even dealt with the assignment problems, as well.
 
so you basically want to remove all Dim statements from the selection context?
 
no, not really. It'd be conditional
Look at the other variable y
it's not in the selection
so if I made it a local variable, it needs to add the Dim y As Long to the new method
which already works (but as a string, not using rewriter)
so when users make changes, I need to account for both missing Dim statements not in the selection and the existing Dim statements within the selection.
Yes, I could have cheated and just regex'd out Dim so I can put it at the top but that creates more problems
and as I said, I need to think about assignments, too
 
5:15 PM
Now with Cyclomatic Complexity analysis. I’m kind of jealous I didn’t get to code that myself! https://twitter.com/rubberduckvba/status/939584327708545024
 
in the end it wasn't as simple as 3 lines above.
I need to also parse the tokens by newing up the VBAParser then getting it to parse
Going to extract that into a class for reusability
 
5:51 PM
[rubberduck-vba/Rubberduck] bclothier pushed 17 commits to ExtractMethod (only showing some of them below)
[rubberduck-vba/Rubberduck] bclothier pushed commit 6435525a to ExtractMethod: Clean up code, refactor out into an extension method and implement null object pattern.
Delete RubberduckUI.resx

Delete the old RubberduckUI.resx so the right one can be moved
[rubberduck-vba/Rubberduck] web-flow pushed commit 2e69c85c to ExtractMethod: Rename Retailcoder.VBE/UI/RubberduckUI.resx to RetailCoder.VBE/UI/RubberduckUI.resx
[rubberduck-vba/Rubberduck] bclothier pushed commit 2e948884 to ExtractMethod: Trying to get rid of duplicated resx files.
Merge branch 'feature-extract-method' of https://github.com/bclothier/Rubberduck into feature-extract-method

# Conflicts:
# RetailCoder.VBE/UI/RubberduckUI.resx
[rubberduck-vba/Rubberduck] bclothier pushed commit 4ae94c13 to ExtractMethod: Merge branch 'next' of github.com/rubberduck-vba/Rubberduck into feature-extract-method
Merge branch 'next' of https://github.com/Hosch250/Rubberduck into feature-extract-method

# Conflicts:
# RetailCoder.VBE/UI/Refactorings/ExtractMethod/ExtractMethodView.xaml
Additional refactoring as per Vogel's suggestion
Documentation of extenstion methods
Fix a typo
[rubberduck-vba/Rubberduck] bclothier pushed commit 2eb7dda4 to ExtractMethod: Simplify regex expression as per Vogel's suggestion
Merge pull request #3588 from bclothier/feature-extract-method

Feature extract method Update 2
> Update parameter to hold Declaration reference for ease of rewriting
Create a new VBACodeStringParser class to support creating a rewriter without writing to the VBA project
Update the model to use the new class and therefore rewriter
 
 
2 hours later…
7:43 PM
When was the last time that the .g4 files were updated?
I'm just wondering why do we have problems to parse on precompiling directives
 
A while, @NelsonVides.
RE: precompiling directive - as I understood it, handling it would make for very complicated grammar structure
we do kind of parse it
but in 2 passes
1st pass, we parse from an exported file, which also include hidden instructions that are not revealed on the code panes
2nd we parse what we see on the code pane
That is necessary because when users use refactoring, their selection has to correspond to what they see on the screen
the 1st pass won't work because it'll be all out of the alignment
The way it is right now, we simply whitespace out "dead code"
so for this given code...
Debug.Print "a"
#If Foo Then
Debug.Print "b"
#Else
Debug.Print "c"
#End If
Debug.Print "d"
assuming the "Foo" is not defined or is false, this is what it'd look like in the Code Pane pass....
Debug.Print "a"



Debug.Print "c"

Debug.Print "d"
This way, we should be able to generate valid code.
 
but that could be problematic if we have something like
#if Foo then
public sub YesFoo()
#else
public sub NoFoo()
#endif
...
End Sub
 
7:59 PM
yeah. you're now defining 2 entirely different thing that has overlapping contexts
here's a more realistic example, (which I actually do use in my production code....)
#If LateBind Then
Public Function CreateMailMessage(MailBody As String) As Object
  Dim OutlookApp As Object

  Const olMailItem As Long = 1
#Else
Public CreateMailMessage(MailBody As String) As Outlook.MailItem
  Dim OutlookApp As Outlook.Application
#End If

  Set OutLookApp = GetOutlookApp()
  Set CreateMailMessage = OutlookApp.CreateItem(olMailItem)
End Function
(simplified a bit but the basic idea is still the same. )
so this procedure has 2 entirely different parse tree, depending on which path you go
but they'd have same overlapping contexts for the 3 last lines
but the contexts won't be at the same index, due to extra statements in the #LateBind part pushing them off one down
getting the grammar to support all that would be.... brutal.
In theory, if you really wanted to be able to consume them all, the parser should return mulitple tree, one for each possible combination of conditional compilation arguments.... which is also very complicated....
 
is there an issue for assigning an incompatible return value? Eg.
Function Foo() As Collection
    Set Foo = Excel.Application
End Function
 
sort of?
I reported something else in....
your is even worse, though.
 
yeah, I was actually returning a different interface from what was declared in the method, and couldn't tell whether RD had just been smart enough to know about the polymorphism.
 
it occurs to me that this is also a fundamental problem with COM, as well
there is no method for enumerating all interfaces an object may support
you can QueryInterface it for anything but there's no promise that you'll get something back and it is possible for several COM objects to have separate hierarchies
 
@this, btw, I guess another approach to the factory validation thing, might be to parse in some validation rules to the factory method. eg. Here's the expected list of headers.
 
8:11 PM
@this what if we can actually evaluate the conditional and return only the parse tree that applies?
 
@NelsonVides we are kind of already doing that....
 
@this unless you're RD, and enumerating the implemented interfaces of a class.
 
There's another problem, though. If the conditional argument is defined in the project, there's no API for reading it....
@ThunderFrame yeah. We can have it enumerate against what we already have had referenced so the list stay short-ish.
or simply brute-force by testing the assignment
that wouldn't require any enumerations
only to validate that QueryInterface won't fail
@NelsonVides but that's also what @ThunderFrame is working on, I think - he's trying to reverse engineer the binary files, so that we can read from the places that we don't have access via the API
and thus build parse tree that respect the current set conditional compilation arguments
If you do not use project conditional compilation arguments, then RD should already build correct parse tree since AIUI, it reads the #Const statement and apply the logic to give you the parse tree with active code path
 
@this Yep, the forms API doesn't tell you the type of the control, and while we could use a Select Case block, and use Type Of, for the built-in controls, it doesn't help with the 3rd-party controls.
 
in my case, I do use it all the time, which means when RD parses my code, the #LateBind is always evaluated to false and thus #Else branch is considered to be the "live" code.
@ThunderFrame, you don't get anything like CustomClass or OLEObject?
With Access.CustomControl, it will tell you exactly what OLE control it's containing with those properties.
@NelsonVides but even so this still imposes a significant problem. For one, it makes refactoring wonky -- if you select in the dead code, it doesn't work (it sees whitespace), and if you refactor the live code, you're going to break the flow of the preprocessing.
 
8:18 PM
@this yes, IIRC, Access gives you the detail, but MSForms doesn't.
 
Consistency, thy name is Microsoft. :\
 
You can get the TypeName, but t's not qualified by library, so you can't tell Borland.Textbox from IBM.TextBox
 
Will it be non-issue once you've parsed the frx file?
 
Dec 6 at 6:09, by ThunderFrame
Or maybe it was the "Word standards or GTFO team".
@this Have progressed to parsing the built-in controls
 
Yeah, that'd be bad. I usually insist on two-part naming because I don't want to deal with "from where is that"
Fantastic!
The worst case would be that you'd build a list of enumerable controls based on the referenced projects for the current VBA project. That'd get you the IBM.Textbox and Borland.Textbox loaded then compared to this mysterious Textbox whenever we encounter an assignment in the VBA code.
I really hope it won't come down that, though. RD shouldn't be playing Guess Who? with the VBA code.
 
8:25 PM
@this drives me crazy in Access/VBA when you hit F2 on a control identifier, and it complains that it can't find the declaration
 
Buy few purple velvet suits from Mat's, so he can get avalon out sooner. :)
 
I was looking to add an initializer method to a class, without making it public on the class or the main interface, so I came up with an extra interface that is only used by the factory method...
'Class OrderSheetProxy
Implements IOrders
Implements IOrdersInitializer

Private Type TOrderSheetProxy
  Sheet As Worksheet
End Type

Private this As TOrderSheetProxy

Private Property Get IOrders_Orders() As Variant
  IOrders_Orders = this.Sheet.UsedRange.Value2
End Property

Private Sub IOrdersInitializer_Initialize(sht As Worksheet)
  Set this.Sheet = sht
End Sub
'Class ProxyFactory
Public Function orders(sht As Worksheet) As IOrders

  Dim temp As IOrdersInitializer
  Set temp = New OrderSheetProxy

  temp.Initialize sht

  'Return the polymorphic form of IOrdersInitializer as IOrders
  Set orders = temp

End Function
Is that a crazy pattern?
 
I don't get what this buys you. Factory should create, and sheet is required, so it should be a parameter to the Create method. If you make assignment of a sheet a responsibility of the initializer, you are now in a situation where you can get a useless object because you forgot to initialize it?
 
yes, but sheet needs to be assigned to the instance by the factory method. I could expose an initialize method on the concrete class, or the main interface, but the extra interface allows me to "hide" the method from the concrete class and the main interface.
i.e. the consumer of the IOrders interface doesn't see Initialize in Intellisense or Object Browser
 
But you are already doing that with the Public Function orders(sht As WorkSheet) As IOrders
so I am still having to pass in a worksheet
and this could be done with a private method Initialize within the factory
and I would not then be even able to cast the returned IOrder to IOrderInitializer and break your encapsulation?
 
8:37 PM
yes, you need to pass in a worksheet, but the factory method forces that to be the only way you can do it. The factory still needs to assign the sheet reference to the instance that it will return, so the instance it creates must at least expose a setter, in order for the factory to assign the sheet reference.
 
I see where you're going with it. You don't want the concrete class to expose Worksheet property and htus let me change it
 
@this sure you could cast, and maybe there should be a guard clause in the method.
@this yep
 
8:51 PM
If it's an option, i probably would consider using instancing PublicNotCreatable for the IOrderProxy, and stick that and the factory in a separate VBA project. That way, you don't need a complicated pattern.
 
9:27 PM
@Hosch250 I'm back to this issue. Should I ping you somewhere (where?) outside of the chat?
 
9:51 PM
> VBA will compile the following code, but will cause an error at run-time:

```vb
Function Foo() As Collection
Set Foo = Excel.Application
End Function
```

RD should be able to determine whether the type of the return value assignment is a compatible type. There will need to be some consideration of polymorphism with regard to interfaces, and also possibly with special case objects like Controls and document-type modules.

There might also need to be special handling for `Object`
 
10:32 PM
@ThunderFrame IMO it's useless, the proxy is your worksheet abstraction - the implementation is inherently coupled with a specific worksheet anyway
 
@Mat'sMug AFAICT, your implementation is coupled to the worksheet (by referring to the predeclared worksheet object), but I'm trying to make it work with a sheet that I specify, which necessitates a setter to assign the sheet reference. That way, the proxy can be used for as many sheets as required (as long as they all conform to the interface requirements), as opposed to having to create a proxy for every sheet.
 
That'll only work if the members make sense for the sheet you give it though
and you can't be compile-time sure of that unless your sheet implements an interface, which will create issues
You could initialize it with another proxy though... like my IDataSheet interface which guarantees that there's a ListObject on the wrapped sheet
I kind of very very much simplified things for the blog post =)
 
@Mat'sMug sure, I'm using Excel as the example here, but I was thinking more of Access, where you can have multiple instances of the document-type modules (forms/reports), so using the predeclared instance in the proxy isn't really the best approach in Access, you'd need to assign the instance reference.
 
10:53 PM
Ah, yeah, makes sense. I never think in Access =)
 
I've been thinking about Access and from what I see, MVP pattern and proxy pattern don't fit well with how Access form works.
This is presuming that one wants to use Access' data binding --- if you ain't, then you've destroyed the whole reason for doing it in Access at all.
The main problem is that when you new an Access form, it will make DB calls before you even can do anything else in its Open/Load event.
This implies that if you want to control how the data binding will behave and thus be able to assign a model of some kind, the form must be compiled and distributed unbound. That to me strongly implies some kind of AOP or at least a build process.
but the idea of doing AOP, I don't know, makes it more complicated than it should be. It makes the code not exactly WYSIWYG.
Access reports is a less of a problem because IIRC, there's no DB calls in the Open event, so at least you have some control there.
The fact that one has to defer the data binding and assign it programmatically also makes it impossible to enforce by interfaces alone.
There's also another problem. Nobody can receive the Open or Load event of an Access Form outside the form itself. Therefore, to do the setup, the Access.Form has to implement something in its event procedure.
@ThunderFrame, FWIW, this is a common pattern I use when I need to extend functionalities in an Access.Form....
Private navigator As KeyNavigator

Private Sub Form_Load()
  Set navigator = New KeyNavigator
End Sub


`Class KeyNavigator

Private WithEvents frm As Access.Form

Private Sub Class_Initialize()
  If TypeOf CodeContextObject Is Access.Form Then
    Set frm = CodeContextObject
  Else
    Err.Raise 5
  End If
  'Initialize stuff here...
End Sub
 
11:23 PM
nice
 
Yeah but as you see, it relies on me to actually implement it. I can't enforce this with interfaces or anything like that at compile-time. That's where I wish I could have real abstract class or maybe partial class in VBA.
That said, writing it out, I could do something like this:
'Interface IDataBinding

'No members...


'Access form code-behind

Implements IDataBinding
Private WithEvents Binding As DataBinding

Private Sub Form_Load()
  Set Binding As New DataBinding
End Sub

'DataBinding class

Private Sub Class_Initialize()
  If TypeOf CodeContextObject Is IDataBinding _
  And TypeOf CodeContextObject Is Access.Form Then
    'Initialize...
  Else
    'Error!
  End If
End Sub
not sure that gains me a lot other than an explicit reminder that IDataBinding implementator shouldn't have any recordsource or rowsource filled when opening.
But that would be at least possible to write a custom RD inspection to catch cases where an Access.Form implements IDataBinding (or whatever) and check if it has non-empty recordsource or rowsources and thus flag an inpsection result.
To make it even more useful, there'd have to be a tool that would detect when form is opened in design form to populate the recordsource & rowsource(s), fill them, and then update a Designer module?procedure? so that one can continue to use the Access GUI for designing forms but still be able to implement a MVVM pattern with an Access form.
I'll lay off the pipe now. :)
 

« first day (1283 days earlier)      last day (1897 days later) »