improved factor plugin
parent
12a09523d4
commit
8b0949dd8b
|
@ -11,15 +11,11 @@
|
||||||
- handle odd base cases, with code after ifte
|
- handle odd base cases, with code after ifte
|
||||||
- handle recursion with when, when* etc
|
- handle recursion with when, when* etc
|
||||||
|
|
||||||
+ linearizer/generator:
|
+ compiler:
|
||||||
|
|
||||||
- getenv/setenv: if literal arg, compile as a load/store
|
- getenv/setenv: if literal arg, compile as a load/store
|
||||||
|
|
||||||
+ compiler frontend:
|
|
||||||
|
|
||||||
- assembler opcodes dispatch on operand types
|
- assembler opcodes dispatch on operand types
|
||||||
- save code in image
|
- save code in image
|
||||||
- compile word twice; no more 'cannot compile' error!
|
|
||||||
|
|
||||||
+ oop:
|
+ oop:
|
||||||
|
|
||||||
|
@ -36,7 +32,6 @@
|
||||||
|
|
||||||
+ listener/plugin:
|
+ listener/plugin:
|
||||||
|
|
||||||
- use decl wrong
|
|
||||||
- faster completion
|
- faster completion
|
||||||
- sidekick: still parsing too much
|
- sidekick: still parsing too much
|
||||||
- errors don't always disappear
|
- errors don't always disappear
|
||||||
|
@ -53,10 +48,9 @@
|
||||||
- cat, reverse-cat primitives
|
- cat, reverse-cat primitives
|
||||||
- first-class hashtables
|
- first-class hashtables
|
||||||
- rewrite accessors and mutators in Factor, with slot/set-slot primitive
|
- rewrite accessors and mutators in Factor, with slot/set-slot primitive
|
||||||
- replace -export-dynamic with sundry-xt
|
|
||||||
- add a socket timeout
|
- add a socket timeout
|
||||||
- word, writes entire plist
|
|
||||||
- do transfer-word in fixup
|
- do transfer-word in fixup
|
||||||
|
- move dispatch getenv setenv to kernel-internals
|
||||||
|
|
||||||
+ misc:
|
+ misc:
|
||||||
|
|
||||||
|
@ -65,7 +59,6 @@
|
||||||
- perhaps /i should work with all numbers
|
- perhaps /i should work with all numbers
|
||||||
- unit test weirdness: 2 lines appears at end
|
- unit test weirdness: 2 lines appears at end
|
||||||
- jedit ==> jedit-word, jedit takes a file name
|
- jedit ==> jedit-word, jedit takes a file name
|
||||||
- command line parsing cleanup
|
|
||||||
- nicer way to combine two paths
|
- nicer way to combine two paths
|
||||||
- ditch object paths
|
- ditch object paths
|
||||||
- browser responder for word links in HTTPd; inspect responder for
|
- browser responder for word links in HTTPd; inspect responder for
|
||||||
|
|
|
@ -207,8 +207,27 @@ public class DefaultVocabularyLookup implements VocabularyLookup
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ getCompletions() method
|
//{{{ getCompletions() method
|
||||||
public void getCompletions(String vocab, String word, Set completions,
|
/**
|
||||||
boolean anywhere)
|
* @param use A list of vocabularies.
|
||||||
|
* @param word A substring of the word name to complete
|
||||||
|
* @param anywhere If true, matches anywhere in the word name are
|
||||||
|
* returned; otherwise, only matches from beginning.
|
||||||
|
* @param completions Set to add completions to
|
||||||
|
*/
|
||||||
|
public void getCompletions(Cons use, String word, boolean anywhere,
|
||||||
|
Set completions) throws Exception
|
||||||
|
{
|
||||||
|
while(use != null)
|
||||||
|
{
|
||||||
|
String vocab = (String)use.car;
|
||||||
|
getCompletions(vocab,word,anywhere,completions);
|
||||||
|
use = use.next();
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ getCompletions() method
|
||||||
|
public void getCompletions(String vocab, String word, boolean anywhere,
|
||||||
|
Set completions) throws Exception
|
||||||
{
|
{
|
||||||
Map v = (Map)vocabularies.get(vocab);
|
Map v = (Map)vocabularies.get(vocab);
|
||||||
if(v == null)
|
if(v == null)
|
||||||
|
|
|
@ -178,6 +178,19 @@ public class ExternalFactor extends DefaultVocabularyLookup
|
||||||
return vocabs;
|
return vocabs;
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ makeWord() method
|
||||||
|
/**
|
||||||
|
* Make a word from an info list returned by Factor.
|
||||||
|
*/
|
||||||
|
public synchronized FactorWord makeWord(Cons info)
|
||||||
|
{
|
||||||
|
FactorWord w = new FactorWord(
|
||||||
|
(String)info.car,
|
||||||
|
(String)info.next().car);
|
||||||
|
w.stackEffect = (String)info.next().next().car;
|
||||||
|
return w;
|
||||||
|
} //}}}
|
||||||
|
|
||||||
//{{{ searchVocabulary() method
|
//{{{ searchVocabulary() method
|
||||||
/**
|
/**
|
||||||
* Search through the given vocabulary list for the given word.
|
* Search through the given vocabulary list for the given word.
|
||||||
|
@ -197,16 +210,11 @@ public class ExternalFactor extends DefaultVocabularyLookup
|
||||||
Cons result = parseObject(eval(FactorReader.unparseObject(name)
|
Cons result = parseObject(eval(FactorReader.unparseObject(name)
|
||||||
+ " "
|
+ " "
|
||||||
+ FactorReader.unparseObject(vocabulary)
|
+ FactorReader.unparseObject(vocabulary)
|
||||||
+ " jedit-lookup ."));
|
+ " search jedit-lookup ."));
|
||||||
if(result.car == null)
|
if(result.car == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
result = (Cons)result.car;
|
return makeWord((Cons)result.car);
|
||||||
w = new FactorWord(
|
|
||||||
(String)result.car,
|
|
||||||
(String)result.next().car);
|
|
||||||
w.stackEffect = (String)result.next().next().car;
|
|
||||||
return w;
|
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
|
@ -216,41 +224,33 @@ public class ExternalFactor extends DefaultVocabularyLookup
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ getCompletions() method
|
//{{{ getCompletions() method
|
||||||
public synchronized void getCompletions(String vocab, String word, Set completions,
|
public synchronized void getCompletions(Cons use, String word,
|
||||||
boolean anywhere)
|
boolean anywhere, Set completions) throws Exception
|
||||||
{
|
{
|
||||||
super.getCompletions(vocab,word,completions,anywhere);
|
super.getCompletions(use,word,anywhere,completions);
|
||||||
|
|
||||||
if(closed)
|
if(closed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
/* We can't send words across the socket at this point in
|
/* We can't send words across the socket at this point in
|
||||||
human history, because of USE: issues. so we send name/vocab
|
human history, because of USE: issues. so we send name/vocab
|
||||||
pairs. */
|
pairs. */
|
||||||
Cons moreCompletions = (Cons)parseObject(eval(
|
Cons moreCompletions = (Cons)parseObject(eval(
|
||||||
FactorReader.unparseObject(word)
|
FactorReader.unparseObject(word)
|
||||||
+ " "
|
+ " "
|
||||||
+ FactorReader.unparseObject(vocab)
|
+ FactorReader.unparseObject(Boolean.valueOf(anywhere))
|
||||||
+ " "
|
+ " "
|
||||||
+ (anywhere ? "vocab-apropos" : "vocab-completions")
|
+ FactorReader.unparseObject(use)
|
||||||
+ " [ dup word-name swap word-vocabulary 2list ] map .")).car;
|
+ " completions .")).car;
|
||||||
|
|
||||||
while(moreCompletions != null)
|
while(moreCompletions != null)
|
||||||
{
|
{
|
||||||
Cons completion = (Cons)moreCompletions.car;
|
Cons completion = (Cons)moreCompletions.car;
|
||||||
FactorWord w = searchVocabulary(completion.next(),
|
FactorWord w = makeWord(completion);
|
||||||
(String)completion.car);
|
|
||||||
if(w != null)
|
if(w != null)
|
||||||
completions.add(w);
|
completions.add(w);
|
||||||
moreCompletions = moreCompletions.next();
|
moreCompletions = moreCompletions.next();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
Log.log(Log.ERROR,this,e);
|
|
||||||
}
|
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ close() method
|
//{{{ close() method
|
||||||
|
|
|
@ -42,15 +42,25 @@ public interface VocabularyLookup
|
||||||
|
|
||||||
public void forget(FactorWord word);
|
public void forget(FactorWord word);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param use A list of vocabularies.
|
||||||
|
* @param word A substring of the word name to complete
|
||||||
|
* @param anywhere If true, matches anywhere in the word name are
|
||||||
|
* returned; otherwise, only matches from beginning.
|
||||||
|
* @param completions Set to add completions to
|
||||||
|
*/
|
||||||
|
public void getCompletions(Cons use, String word, boolean anywhere,
|
||||||
|
Set completions) throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param vocab The vocabulary name
|
* @param vocab The vocabulary name
|
||||||
* @param word A substring of the word name to complete
|
* @param word A substring of the word name to complete
|
||||||
* @param completions List to add completions to
|
|
||||||
* @param anywhere If true, word name will be matched anywhere, otherwise, just at
|
* @param anywhere If true, word name will be matched anywhere, otherwise, just at
|
||||||
* the beginning of the name.
|
* the beginning of the name.
|
||||||
|
* @param completions Set to add completions to
|
||||||
*/
|
*/
|
||||||
public void getCompletions(String vocab, String word, Set completions,
|
public void getCompletions(String vocab, String word, boolean anywhere,
|
||||||
boolean anywhere) throws Exception;
|
Set completions) throws Exception;
|
||||||
|
|
||||||
public Cons getVocabularies() throws Exception;
|
public Cons getVocabularies() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,8 @@ public class EditWordDialog extends WordListDialog
|
||||||
list.setSelectedIndex(0);
|
list.setSelectedIndex(0);
|
||||||
list.ensureIndexIsVisible(0);
|
list.ensureIndexIsVisible(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePreview();
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ UpdateTimer class
|
//{{{ UpdateTimer class
|
||||||
|
|
|
@ -61,14 +61,23 @@ public class FactorCompletion extends SideKickCompletion
|
||||||
|
|
||||||
public void insert(int index)
|
public void insert(int index)
|
||||||
{
|
{
|
||||||
Macros.Recorder recorder = view.getMacroRecorder();
|
FactorWord selected = ((FactorWord)get(index));
|
||||||
|
String insert = selected.name.substring(word.length());
|
||||||
|
|
||||||
String insert = ((FactorWord)get(index)).name.substring(
|
Buffer buffer = textArea.getBuffer();
|
||||||
word.length());
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
buffer.beginCompoundEdit();
|
||||||
|
|
||||||
if(recorder != null)
|
|
||||||
recorder.recordInput(insert,false);
|
|
||||||
textArea.setSelectedText(insert);
|
textArea.setSelectedText(insert);
|
||||||
|
if(!FactorPlugin.isUsed(view,selected.vocabulary))
|
||||||
|
FactorPlugin.insertUse(view,selected.vocabulary);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
buffer.endCompoundEdit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTokenLength()
|
public int getTokenLength()
|
||||||
|
@ -81,13 +90,7 @@ public class FactorCompletion extends SideKickCompletion
|
||||||
if(keyChar == '\t' || keyChar == '\n')
|
if(keyChar == '\t' || keyChar == '\n')
|
||||||
insert(selectedIndex);
|
insert(selectedIndex);
|
||||||
else
|
else
|
||||||
{
|
|
||||||
Macros.Recorder recorder = view.getMacroRecorder();
|
|
||||||
|
|
||||||
if(recorder != null)
|
|
||||||
recorder.recordInput(1,keyChar,false);
|
|
||||||
textArea.userInput(keyChar);
|
textArea.userInput(keyChar);
|
||||||
}
|
|
||||||
|
|
||||||
boolean ws = (ReadTable.DEFAULT_READTABLE
|
boolean ws = (ReadTable.DEFAULT_READTABLE
|
||||||
.getCharacterType(keyChar)
|
.getCharacterType(keyChar)
|
||||||
|
|
|
@ -73,6 +73,13 @@ public class FactorPlugin extends EditPlugin
|
||||||
public void stop()
|
public void stop()
|
||||||
{
|
{
|
||||||
stopExternalInstance();
|
stopExternalInstance();
|
||||||
|
|
||||||
|
Buffer buffer = jEdit.getFirstBuffer();
|
||||||
|
while(buffer != null)
|
||||||
|
{
|
||||||
|
buffer.setProperty(FactorSideKickParser.PARSED_PROPERTY,null);
|
||||||
|
buffer = buffer.getNext();
|
||||||
|
}
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ getExternalInstance() method
|
//{{{ getExternalInstance() method
|
||||||
|
@ -165,9 +172,9 @@ public class FactorPlugin extends EditPlugin
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ evalInWire() method
|
//{{{ evalInWire() method
|
||||||
public static void evalInWire(String cmd) throws IOException
|
public static String evalInWire(String cmd) throws IOException
|
||||||
{
|
{
|
||||||
getExternalInstance().eval(cmd);
|
return getExternalInstance().eval(cmd);
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ lookupWord() method
|
//{{{ lookupWord() method
|
||||||
|
@ -275,37 +282,15 @@ public class FactorPlugin extends EditPlugin
|
||||||
* returned; otherwise, only matches from beginning.
|
* returned; otherwise, only matches from beginning.
|
||||||
*/
|
*/
|
||||||
public static Set getCompletions(String word, boolean anywhere)
|
public static Set getCompletions(String word, boolean anywhere)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return getCompletions(getExternalInstance().getVocabularies(),word,
|
|
||||||
anywhere);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
} //}}}
|
|
||||||
|
|
||||||
//{{{ getCompletions() method
|
|
||||||
/**
|
|
||||||
* @param anywhere If true, matches anywhere in the word name are
|
|
||||||
* returned; otherwise, only matches from beginning.
|
|
||||||
*/
|
|
||||||
public static Set getCompletions(Cons use, String word, boolean anywhere)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Set completions = new HashSet();
|
Set completions = new HashSet();
|
||||||
|
|
||||||
while(use != null)
|
|
||||||
{
|
|
||||||
String vocab = (String)use.car;
|
|
||||||
getExternalInstance().getCompletions(
|
getExternalInstance().getCompletions(
|
||||||
vocab,word,completions,anywhere);
|
getExternalInstance().getVocabularies(),
|
||||||
use = use.next();
|
word,
|
||||||
}
|
anywhere,
|
||||||
|
completions);
|
||||||
return completions;
|
return completions;
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
|
@ -375,7 +360,7 @@ public class FactorPlugin extends EditPlugin
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
//{{{ isUsed() method
|
//{{{ isUsed() method
|
||||||
private static boolean isUsed(View view, String vocab)
|
public static boolean isUsed(View view, String vocab)
|
||||||
{
|
{
|
||||||
SideKickParsedData data = SideKickParsedData
|
SideKickParsedData data = SideKickParsedData
|
||||||
.getParsedData(view);
|
.getParsedData(view);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
plugin.factor.jedit.FactorPlugin.activate=startup
|
plugin.factor.jedit.FactorPlugin.activate=startup
|
||||||
|
|
||||||
plugin.factor.jedit.FactorPlugin.name=Factor
|
plugin.factor.jedit.FactorPlugin.name=Factor
|
||||||
plugin.factor.jedit.FactorPlugin.version=0.70
|
plugin.factor.jedit.FactorPlugin.version=0.71
|
||||||
plugin.factor.jedit.FactorPlugin.author=Slava Pestov
|
plugin.factor.jedit.FactorPlugin.author=Slava Pestov
|
||||||
plugin.factor.jedit.FactorPlugin.docs=/doc/jedit/index.html
|
plugin.factor.jedit.FactorPlugin.docs=/doc/jedit/index.html
|
||||||
|
|
||||||
|
|
|
@ -290,8 +290,7 @@ public class FactorSideKickParser extends SideKickParser
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
FactorWord[] completions = FactorPlugin.toWordArray(
|
FactorWord[] completions = FactorPlugin.toWordArray(
|
||||||
FactorPlugin.getCompletions(
|
FactorPlugin.getCompletions(word,false));
|
||||||
data.use,word,false));
|
|
||||||
|
|
||||||
if(completions.length == 0)
|
if(completions.length == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -31,16 +31,19 @@ package factor.jedit;
|
||||||
|
|
||||||
import factor.*;
|
import factor.*;
|
||||||
import javax.swing.border.*;
|
import javax.swing.border.*;
|
||||||
|
import javax.swing.event.*;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import org.gjt.sp.jedit.gui.EnhancedDialog;
|
import org.gjt.sp.jedit.gui.EnhancedDialog;
|
||||||
import org.gjt.sp.jedit.*;
|
import org.gjt.sp.jedit.*;
|
||||||
|
import org.gjt.sp.util.Log;
|
||||||
|
|
||||||
public abstract class WordListDialog extends EnhancedDialog
|
public abstract class WordListDialog extends EnhancedDialog
|
||||||
{
|
{
|
||||||
protected View view;
|
protected View view;
|
||||||
protected JList list;
|
protected JList list;
|
||||||
|
protected JTextArea preview;
|
||||||
protected JButton ok, cancel;
|
protected JButton ok, cancel;
|
||||||
|
|
||||||
//{{{ WordListDialog constructor
|
//{{{ WordListDialog constructor
|
||||||
|
@ -55,13 +58,47 @@ public abstract class WordListDialog extends EnhancedDialog
|
||||||
content.setBorder(new EmptyBorder(12,12,12,12));
|
content.setBorder(new EmptyBorder(12,12,12,12));
|
||||||
setContentPane(content);
|
setContentPane(content);
|
||||||
|
|
||||||
content.add(BorderLayout.CENTER,new JScrollPane(
|
JScrollPane listScroll = new JScrollPane(
|
||||||
list = new JList()));
|
list = new JList());
|
||||||
list.setCellRenderer(new FactorWordRenderer(parser,true));
|
list.setCellRenderer(new FactorWordRenderer(parser,true));
|
||||||
|
list.addListSelectionListener(new ListHandler());
|
||||||
|
|
||||||
|
JScrollPane previewScroll = new JScrollPane(
|
||||||
|
preview = new JTextArea(12,60));
|
||||||
|
preview.setEditable(false);
|
||||||
|
|
||||||
|
listScroll.setPreferredSize(previewScroll.getPreferredSize());
|
||||||
|
|
||||||
|
JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
|
||||||
|
listScroll,previewScroll);
|
||||||
|
split.setDividerLocation(0.5);
|
||||||
|
split.setResizeWeight(0.5);
|
||||||
|
content.add(BorderLayout.CENTER,split);
|
||||||
|
|
||||||
content.add(BorderLayout.SOUTH,createButtonPanel());
|
content.add(BorderLayout.SOUTH,createButtonPanel());
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ updatePreview() method
|
||||||
|
protected void updatePreview()
|
||||||
|
{
|
||||||
|
FactorWord word = (FactorWord)list.getSelectedValue();
|
||||||
|
if(word == null)
|
||||||
|
{
|
||||||
|
preview.setText("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
preview.setText(FactorPlugin.evalInWire(
|
||||||
|
FactorPlugin.factorWord(word) + " see").trim());
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
Log.log(Log.ERROR,this,e);
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
|
|
||||||
//{{{ createButtonPanel() method
|
//{{{ createButtonPanel() method
|
||||||
private Box createButtonPanel()
|
private Box createButtonPanel()
|
||||||
{
|
{
|
||||||
|
@ -91,4 +128,13 @@ public abstract class WordListDialog extends EnhancedDialog
|
||||||
cancel();
|
cancel();
|
||||||
}
|
}
|
||||||
} //}}}
|
} //}}}
|
||||||
|
|
||||||
|
//{{{ ListHandler class
|
||||||
|
class ListHandler implements ListSelectionListener
|
||||||
|
{
|
||||||
|
public void valueChanged(ListSelectionEvent evt)
|
||||||
|
{
|
||||||
|
updatePreview();
|
||||||
|
}
|
||||||
|
} //}}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,10 +95,10 @@ C: jedit-stream ( stream -- stream )
|
||||||
#! Execute this in the inferior Factor.
|
#! Execute this in the inferior Factor.
|
||||||
stdio [ <jedit-stream> ] change print-banner ;
|
stdio [ <jedit-stream> ] change print-banner ;
|
||||||
|
|
||||||
: jedit-lookup ( word vocabs -- )
|
: jedit-lookup ( word -- list )
|
||||||
#! A utility word called by the Factor plugin to get some
|
#! A utility word called by the Factor plugin to get some
|
||||||
#! required word info.
|
#! required word info.
|
||||||
search dup [
|
dup [
|
||||||
[
|
[
|
||||||
"vocabulary"
|
"vocabulary"
|
||||||
"name"
|
"name"
|
||||||
|
@ -107,3 +107,16 @@ C: jedit-stream ( stream -- stream )
|
||||||
dupd word-property
|
dupd word-property
|
||||||
] map nip
|
] map nip
|
||||||
] when ;
|
] when ;
|
||||||
|
|
||||||
|
: completions ( str anywhere vocabs -- list )
|
||||||
|
#! Make a list of completions. Each element of the list is
|
||||||
|
#! a name/vocabulary pair.
|
||||||
|
[
|
||||||
|
[
|
||||||
|
>r 2dup r> swap [
|
||||||
|
vocab-apropos
|
||||||
|
] [
|
||||||
|
vocab-completions
|
||||||
|
] ifte [ jedit-lookup , ] each
|
||||||
|
] each
|
||||||
|
] make-list ;
|
||||||
|
|
Loading…
Reference in New Issue