I have been watching OdatNurd youtube plugin dev tutorials on gathering text.
I want to execute a command palette command via a mapped key
The json command file:
[
{
"caption": "A sample command",
"command": "example",
"args": {
// "message": "Sample",
// "position": 0
},
},
]
Key mapping for my command
{ "keys": ["ctrl+3"], "command": "example", "args": {"message": "1"}}
The issue I have is that I have 2 arguments in my sublime-command json file and I don't want to pass one of the values, but execution via the key mapped complains that the command wants 2 argument and I have passed 1.
I don't know which videos you're watching as you didn't include any links, but one option to try would be to use a null value for the option you don't want to send (for some reason?). For example:
{ "keys": ["ctrl+3"], "command": "example", "args": {"message": "1", "position": null}}
In Python, this translates to
example(message="1", position=None)
I don't know the structure of your class ExampleCommand, so I don't know if it'll choke on the null/None, but it's worth trying.
It's a little unclear what might be going wrong for you because you haven't provided the example implementation of the command that you're using. I can come up with a likely scenario for what's going wrong based on the symptoms you've described however.
For the sake of clarity here (now and for anyone that hasn't watched the associated videos), here's an example of the plugin as it appears in the first of the videos you referenced, along with an associated command palette entry and key binding for it:
Plugin
import sublime
import sublime_plugin
class MessageInputHandler(sublime_plugin.TextInputHandler):
def next_input(self, args):
if 'position' not in args:
return PositionInputHandler()
class PositionInputHandler(sublime_plugin.ListInputHandler):
def list_items(self):
return [
('Top of file', 0),
('Botto of file', -1)
]
class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit, message, position):
if position < 0:
position = self.view.size()
self.view.insert(edit, position, message)
def input(self, args):
if 'message' not in args:
return MessageInputHandler()
elif 'position' not in args:
return PositionInputHandler()
Command palette entry
{ "caption": "An example command", "command": "example" },
Key Binding
{ "keys": ["ctrl+3"], "command": "example", "args": {
"message": "1"
}},
As defined here, the command takes two arguments, message that is some text to insert, and position that is the location to insert it at.
Since the run method declares that the command takes two arguments, in the normal state of affairs executing the command without providing both arguments will cause a Python error because arguments to commands are passed to run(), and without all of the arguments the call to the method is incorrect.
Using the input() method in conjunction with having the command in the command palette changes this behaviour; Internally Sublime will catch the error, notice that the command implements input() and then invoke it to see if it should be prompting the user via the Command Palette for any missing arguments.
The important parts here are that this will only happen if the command is in the command palette, and that the arguments that get prompted for are entirely under the control of the command itself, since Sublime can't make a general determination on what the missing arguments are supposed to represent.
Using the example of the plugin above, executing the command via the command palette entry has it ask you first for the text to insert, and then for the location in the list input handler.
Using the key binding also invokes the command, but now the prompt you get jumps directly to the ListInputHandler asking you where to do the insertion, skipping over the prompt for the text.
Why? Because the implementation of input() inspects the arguments that were provided, and only prompts you for message if the argument isn't given; since the key binding provides that argument, it skips to checking for the position argument, finds it missing, and prompts you for that instead.
Per your original question:
The issue I have is that I have 2 arguments in my sublime-command json file and I don't want to pass one of the values, but execution via the key mapped complains that the command wants 2 argument and I have passed 1.
It's important first to note that regarding the entry in the command palette, the arguments presented there are only used when the command is actually executed via the command palette. When executed via the key binding, nothing about the command palette configuration is used (except that the entry has to exist).
So, all else being equal based on your original question, the key binding should work as expected so long as the input() method exists and knows to ask for the right arguments.
For example, if input() is not defined, then Sublime can't prompt you, and you get an error message:
Traceback (most recent call last):
File "/home/tmartin/local/sublime_text_4101/Lib/python38/sublime_plugin.py", line 1512, in run_
return self.run(edit, **args)
TypeError: run() missing 1 required positional argument: 'position'
Or, if the input() method is defined but doesn't know that it should look for the missing argument and prompt you for it, that will also cause a problem:
def input(self, args):
if 'message' not in args:
return MessageInputHandler()
In this case the method is invoked, but since it's not looking to see if position is an argument that was missing, it falls off the bottom returning None, Sublime doesn't prompt you, and you get the same error as above.
In the comments above you also mentioned this:
I don't think I have explained the problem very well. I have set up a command which I execute using the Sublime standard shift/command/p It uses textinputhandler and a listinputhandler. It, and the associated python plugin work fine using shit/cmd/p but if I set up my own keyboard mapping, I have to provide both arguments (the text and the list entry). I can't find a way of triggering the listinputhandler when a value has been provided by the key mapping even if that value is None
Again, without seeing the code it's hard to say with any exactness what might be happening. However, the fact that this works for you with that command palette entry (providing no arguments) but not from the key binding points to an issue with your input() method.
In particular, input() is called only once, and is expected to return an instance of an InputHandler for a specific argument. The input handler that input() returns is used to prompt for a specific argument, and when the user is done, the next_input() on the input handler is invoked to ask it "where do we go from here?"
As defined in the example plugin above and in the video, the MessageInputHandler responds to this by saying "if the position argument is also missing, ask for that next".
So, if this works from the command palette and not from the key binding, the most likely reason is that the input method is wrong; from the command palette having both items missing causes input() to ask for message, and message then asks for position, whereas from the key binding with message provided, input() must be returning None, stopping it from asking for position and causing your issue.
Related
I have a variable in Swift code that runs in iOS simulator and contains an existing fileURL. I want to have the file opened in macOS (not the iOS Simulator) when I hit a breakpoint.
I added an action "Shell Command" to the breakpoint to open the file. The file exists because if I copy-paste the file's path to Terminal, it opens in Preview.
However, the Xcode console says the contrary:
The file /"/Users/tomkraina/Library/Developer/CoreSimulator/Devices/FBA16E00-9450-40E8-9650-1489A67E344C/data/Containers/Data/Application/BB97DB72-FF2A-4087-BD42-2934C63D3323/tmp/7E2303B8-0629-475A-862A-2550351FB448/OutlineExport.pdf" does not exist.
First Question: How do I tell Xcode to open a file with provided fileURL in a variable on breakpoint?
Next thing I tried was to open the file using LLDB, but I cannot find out how to evaluate a command parameter in LLDB, because backticks is only for scalars:
(lldb) shell open `temporaryFile.fileURL.path`
The file /105553157711856 does not exist.
Second Question: How to I evaluate argument parameter to get a string in LLDB?
I don't have a good answer for the first question. It would be interesting to check whether the path that is in the Xcode error message is correct - maybe it's getting it from the value incorrectly. If you copy the path from the error message, go to Terminal and try to open it, does that work? Anyway, this sounds to me like a bug in Xcode. It got some kind of path out of your variable and tried to open it, which should have worked. If you want to follow up, it's probably best to file a bug report with the Apple Feedback.
For the second question, you have to know a little about how variables work in lldb. Some variables have obvious values, for instance, in C a pointer has the pointer value, an integer the integer value, etc. Other variables (any kind of Struct being the obvious example) are actually containers of other values and don't really have a "value" themselves.
lldb can show you what a swift string really is using the --raw option:
(lldb) v --raw str1
(Swift.String) str1 = {
_guts = {
_object = {
_countAndFlagsBits = {
_value = -3458764513820540912
}
_object = 0x8000000100003f50 (0x0000000100003f50) strings`symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> () + 4
}
}
}
That's probably really interesting to people working on the Swift Standard Library and has the virtue of being the truth. But for most purposes, it's not a terribly useful representation.
lldb handles that problem by adding a notion of "Summary Formatters" that generate a string representation for objects based on their type. There's one for "Swift.string" that digs around in the object, finds where the actual string is, and returns that text. If you don't pass --raw and there's a summary formatter, then lldb will show you the summary:
(lldb) v str1
(String) str1 = "some string here"
That is also the value that you want to try and open.
The backtick syntax in lldb gets the value of the entity, not its summary, which is why that didn't work for a swift string. However, you can fetch and act on the summaries for local variables using lldb's Python interpreter and the SB API. So for instance:
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> var = lldb.frame.FindVariable("str1")
>>> var.GetSummary()
'"some string here"'
So then if that was a file path you wanted to open, you can use Python to do that, like:
>>> os.system("open {0}".format(var.GetSummary()))
The file /private/tmp/some string here does not exist.
256
except of course your var has to hold the path to a real file...
If you want to learn more about the lldb Python API's the API docs are here:
https://lldb.llvm.org/python_api.html
and a general tutorial for using Python in lldb is here:
https://lldb.llvm.org/use/python-reference.html
And more information on variable formatting is here:
https://lldb.llvm.org/use/variable.html
I intent to use gsdll32 to display postscript in a Win32 window (not ghostview).
I need help with the parameters needed by gsdll_init_with_args.
The function immediately returns error -0x12 or -0x100.
I tried several parameter combinations in various sequences:
-sDisplayFormat=16#030804
-dDisplayHandle="1234"
-dDisplayResolution=96
-sDEVICE=display
postscriptfile.ps
As a second question:
What should the parameters be if I want to pipe in the postscript data programmatically ?
Examples would be nice.
Seppe
The supplied source code for Windows uses gs_dll_init_with_args(). If you look in /ghostpdl/psi/dwmain.c, function new_main(int argc, char *argv) then at about line 328 you can see the call happening.
You can follow this through in a debugger to see what the arguments look like (they get sanitised before arriving here). Get it to work the way you want on the command line, then break here with those arguments and you can see what your own code should be providing.
To send the data buffer-by-buffer, we don't have an example. However, you start by calling gsapi_run_string_begin(), then repeatedly calling gsapi_run_string_continue() until you exhaust the data, then call gsapi_run_string_end(). Obviously you will have to check the return code to see if an error occurred.
Finally; please check the AGPL to be sure you can conform to the license restrictions.
Is there a way to create a single snippet file where the content output is dependent on the language? For example, keyboard shortcut x outputs "abc" when used in a css file, but "def" when used in a javascript file?
Snippets don't contain much processing capability — you can perform substitutions in them via Boost-style regexes and format strings, and they have access to a number of environment variables within Sublime such as the current file's name, the line number, etc., but beyond that they don't have much programmatic processing capability. It might be possible to set up a series of regexes that try to case-insensitively match $TM_FILENAME against \.css$ and output abc, then immediately match $TM_FILENAME against \.js$ and output def - only one of them will be successful.
However, in my mind such processing is much more easily handled by a plugin written in Python. The API documentation is mostly complete (all the functions you'll need are documented there) and there are a ton of examples around the net to learn/borrow from. Here's a quick example:
import sublime_plugin
class PrintScopeCommand(sublime_plugin.TextCommand):
def run(self, edit):
pos = self.view.sel()[0].begin()
scope = self.view.scope_name(pos)
if 'source.js' in scope:
self.view.insert(edit, pos, "This is JavaScript!")
elif 'source.css' in scope:
self.view.insert(edit, pos, "This is CSS!")
Save the file in your Packages/User directory (accessible via Preferences -> Browse Packages...) as print_scope.py. Next, assign it to a key binding by opening your user keymap (Preferences -> Key Bindings-User) and adding the following:
[
{ "keys": ["ctrl+alt+shift+p"], "command": "print_scope" }
]
if the file is empty. If you already have other custom key bindings, just add a comma , after the last one, then paste the line above after it, before the closing square bracket ].
You should now be able to hit CtrlAltShiftP (or whatever other key binding you choose), and if the file's syntax is set to JavaScript it will insert the This is JavaScript! message at the current cursor position. If the syntax is set to CSS, This is CSS! will print, and if the syntax is anything else, then nothing will print.
I have a view set up to accept 2 arguments. The url is like this: playlists/video5/%/%/rss.xml
It works fine if I supply the url with 2 arguments like playlists/video5/front/coach/rss.xml.
I have 2 arguments of "Taxonomy: Term"
But I need it to run even if 1 or no arguments are supplied. It looks like you can do this with PHP Code under: Provide default argument options -> Default argument type: -> PHP Code.
I'm using this for the first one:
$arg[0] == 'all';
return 'all';
I'm using this for the second one:
$arg[1] == 'all';
return 'all';
It's working fine in the preview if I enter 1, 2 or no arguments, but in the browser it giving me a "Page not found" error if I use less than 2 arguments in the url.
It woks with these urls:
/playlists/video5/gridiron/all/rss.xml
/playlists/video5/gridiron/football/rss.xml
It does not work with this:
playlists/video5/gridiron/rss.xml
I want it to return all values when no arguments are given, or if only one arg is given, just use the one, etc...
thanks
I would rearrange your URL to look like this: playlists/video5/rss/%/% so that way your arguments always come last. Then in your argument settings set:
Action to take if argument is not present: Display all values
This way when you go to playlists/video5/rss you will get every value. When you go to /playlists/video5/rss/term1 you will get all values that have term1 in them. Then the trick for the second argument is to include the wildcard for first argument like this: /playlists/video5/rss/all/term2. I believe that will include just the values that have the second term.
Alternatively, if these are both taxonomy terms, you may want to consolidate these into a single argument and check the box that says: Allow multiple terms per argument. According to the documentation right below the checkbox, it looks like this should allow you something like playlists/video5/rss/term1+term2 and display all values that have the first or second term.
Views will only collapse the %, not the slashes surrounding it. So while you're trying to use playlists/video5/rss.xml, Views is expecting playlists/video5///rss.xml.
To get what you're looking for, you need to duplicate the View display you're using twice.
For the first duplicate, use playlists/video5/%/rss.xml as the path. In your arguments for this view display, make sure the first argument validates for either gridiron or football.
For the second duplicate, use playlists/video5/rss.xml. There will be no arguments for this view display. If you just want all of the records to show up, you shouldn't have to do anything more. But if you want to supply a default argument other than all the records, you'd override the view display and create a filter instead of an argument.
Another (less ideal) option is to treat gridiron/football as one argument and validate it that way.
Whilst debugging in Xcode_3.1.2 I am pretty sure I could see the contents of my NSString arrays. However after upgrading to 3.2 I only see the following ...
I know I can print the object in (gdb) using "po planetArray" or simply click in the debugger and "print description to console" I am just curious, as I am sure it worked prior to upgrading. Anyone know anything about this?
cheers gary
edit: data formatters is on and it shows what you see above ...
This is because GDB acts as if the variable you are viewing is out of scope while it really just is confused about what each part function or method call of the data formatter is returning (the data formatter is the "{(unichar *)Xcode_CFStringSummary($VAR, $ID)}:s" part you are seeing.
When you are debugging and you are in a method where you know a local variable must be in scope right now, open the debugger window and the area where you can see "Variable", "Value" and "Summary" column titles double click the "Summary" row entry for the variable you are interested in and enter the following (for array types like NSArray or NSCFArray):
"{(int)[$VAR count]} objects {(NSString *)[(NSArray *)$VAR description]}:s"
then press return. You have now overwritten the default data formatter provided by Xcode's GDB extension (to be found in various plists at "/Developer/Library/Xcode/CustomDataViews/") with your own data formatter string.
Your own overrides are saved at "~/Library/Application Support/Developer/Shared/Xcode/CustomDataViews/CustomDataViews.plist" and if you want to have the Apple default data formatter back just double click the row for a variable of the same type and delete whatever is there.
The nitty-gritty details: In the custom expression above the "{}" construct tells GDB to execute a command (as if you where executing it from GDB's debugger command line, which means the same restrictions apply: you need to specify the return type in cast parens in front of every function or method which returns something). The ":s" behind the closing curly brace tells Xcode and GDB to reference the "Summary" column. Also valid would be ":v" which references the "Value" column which most of the time is just the pointer value. Everything that is outside of the curly braces is shown verbatim.
Unfortuntely curly braces can't be nested which invalidates ternary operator conditionals.
So with the above data formatter you should see the following for an empty NSArray:
"0 objects (\n)"
If you want to write your own data formatters as GDB extensions (equivalent to specifying a function akin to Xcode_CFStringSummary above) you can do so. Take a look at the following header: "/Developer/Applications/Xcode.app/Contents/PlugIns/GDBMIDebugging.xcplugin/Contents/Headers/DataFormatterPlugin.h"
it will tell you all you need to know. But it can be hard to get it right. It might be easier and less error prone to just define another method on your class and call that from the data formatter string instead of "description".
In the Run > Variables View menu in Xcode, is "Use Data Formatters" enabled?
I am not sure if this helps but if you select the array value to wish to see in the debugger window and the go to the Menu : Run > Variables View > View Variable As
you can change it from "NSCFString *" to "NSString *". You then see the value so "Planet_1" for example.
Cheers,
Kevin