On 18-Feb-08, at 5:30 PM, Allan Odgaard wrote:

On 18 Feb 2008, at 02:23, Alain O'Dea wrote:

[...]
I can't get this to work. I am left with text appearing after the end of the scope. For clarification the scope I am working with can be many lines. How do I make TextMate insert the output at the caret instead of at the end of the selection/scope?

Are you using “selection or scope” as input and “insert as snippet” as output?

You will sometimes get more input that what would be ideal (using a scope selector), but there is presently no way around that.

The Objective-C bundle has completion commands that takes current scope as input, you may want to look at the se for examples. If you still cannot get it to work, please post what you are actually doing.


I am building a code completion system for Erlang in TextMate. The process is quite involved, but it gets completions extremely quickly.

command_server (command_server.erl) receives a command name followed by input lines over TCP. If "vars" is sent then command_server invokes vars:exec/2 (vars.erl) which parses the input and returns the variables in it. command_server then invokes tm_menu:get_selection/1 (tm_menu.erl) which builds a plist from the variables lis, invokes tm_dialog, and returns the user's selection once made. command_server then returns the result to the client over TCP. In theory invoking Variable Completion (Command) should insert the selected variable at the caret. Instead it inserts at the end of the scope.

Realizing that TextMate was going to insert at the end of the input I decided to hack around it using Macros. To this end command_server, just before responding to the to TCP client, invokes clipboard:copy/1 which uses pbcopy to add the result to the clipboard. As a result I can invoke Variable Completion into Clipboard (Command) and then Edit- >Paste to drop the result right at the caret. Unfortunately there seems to be a race condition on the clipboard and this macro often drops the previous clipboard contents. This occurs even if the local clipboard option for the Macro is turned off. If I paste again manually the correct value is pasted. Using the Paste (Command) instead of Edit->Paste in the macro works correctly, but with a noticeable lag between selecting the completion and the subsequent insertion.

Listings:
=========

erl_vars.py:
#!/usr/bin/env python
import socket
import sys
import os
import time
module = os.getenv('TM_FILEPATH').split('/').pop().split('.').pop(0)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',2345))
s.send('vars\n')
for line in sys.stdin:
    s.send(line.rstrip() + os.linesep)
s.send('eof' + os.linesep)
# rendezvous with Erlang server
print s.recv(4096)

Variable Completion (Command):
python erl_vars.py
-input: Selected Text or Scope
-output: Insert as Text

Variable Completion into Clipboard (Command):
python erl_vars.py
-input: Selected Text or Scope
-output: None

Paste (Command):
pbpaste
-input: None
-output: Insert as Text

Paste as Snippet (Command):
pbpaste
-input: None
-output: Insert as Snippet

command_server.erl:
-module(command_server).
-export([start/0]).

start() ->
    {ok, Listen} = gen_tcp:listen(2345, [list, {packet, line},
                                         {reuseaddr, true},
                                         {active, true}]),
    spawn(fun() -> par_connect(Listen) end).

par_connect(Listen) ->
    {ok, Socket} = gen_tcp:accept(Listen),
    io:format("Client session started~n"),
    spawn(fun() -> par_connect(Listen) end),
    loop(Socket).

loop(Socket) ->
    receive
        {tcp, Socket, ModuleRaw} ->
            Module = list_to_atom(trim(ModuleRaw)),
            Reply = Module:exec(tm_command, iter(Socket)),
            clipboard:copy(Reply),
            gen_tcp:send(Socket, list_to_binary(Reply)),
            loop(Socket);
        {tcp_closed, Socket} ->
            io:format("Client session ended~n")
    end.

iter(Socket) ->
    fun() ->
        receive
            {tcp, Socket, "eof\n"} ->
                eof;
            {tcp, Socket, Line} ->
                trim(Line);
            {tcp_closed, Socket} ->
                eof
        end
    end.

trim(String) ->
    {ok, Trimmed, _} = regexp:sub(String, "[\r\n]", ""),
    string:strip(Trimmed).

clipboard.erl:
-module(clipboard).
-export([copy/1]).

copy(Data) ->
    Clipboard = open_port({spawn, "pbcopy"}, [stream]),
    Clipboard ! {self(), {command, Data}},
    Clipboard ! {self(), close},
    receive
        {Clipboard, closed} -> true
    end.

tm_menu.erl:
-module(tm_menu).
-export([get_selection/1]).
-include_lib("xmerl/include/xmerl.hrl").

get_selection(MenuItems) ->
    %% TODO: need better way to get path for tm_dialog
PlistXml = os:cmd(io_lib:format("/Applications/TextMate.app/ Contents/PlugIns/Dialog.tmplugin/Contents/Resources/tm_dialog -u -p \"~s\"", [ascii_plist(MenuItems)])),
    {PlistDoc, _Rest} = xmerl_scan:string(PlistXml),
[#xmlText{value=Selected}] = xmerl_xpath:string("/plist/dict/ dict[preceding-sibling::key[1]='selectedMenuItem']/string[preceding- sibling::key[1]='title']/text()", PlistDoc),
    Selected.

ascii_plist(MenuItems) ->
    lists:flatten(
        lists:reverse([");}"|
            lists:foldl(
                fun(Item, Completions) ->
[io_lib:format("{title = ~s;},", [Item])| Completions]
                end,
                ["{menuItems = ("],
                MenuItems
            )
        ])
    ).

vars.erl:
-module(vars).
-export([exec/2]).

exec(tm_command, Iter) ->
    tm_menu:get_selection(list(Iter)).

list(Iter) ->
    sets:to_list(
        sets:from_list(
            list(Iter, Iter()))).

list(_, eof) ->
    [];
list(Iter, Line) ->
    matches(Line, list(Iter, Iter())).

matches(Line, MatchesIn) ->
{match, MatchIndices} = regexp:matches(Line, "[A-Z][a-zA- [EMAIL PROTECTED]"),
    lists:foldl(
        fun({Start, Length}, Matches) ->
            [string:substr(Line, Start, Length)|Matches]
        end,
        MatchesIn, MatchIndices).


_______________________________________________
textmate-dev mailing list
[email protected]
http://lists.macromates.com/mailman/listinfo/textmate-dev

Reply via email to