⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 completeword.em

📁 source insight的EM文件 可以扩展SI的宏功能
💻 EM
字号:
/* --------------------------------------------------------------
# Module CompleteWord version 0.0.1
# Released to the public domain 14-Aug-1999,
# by Tim Peters (tim_one@email.msn.com).

# Provided as-is; use at your own risk; no warranty; no promises; enjoy!

Word completion.
Macro CompleteWord moves forward.
Macro CompleteWordBack moves backward.
Assign to e.g. F12 and Shift+F12.

The word characters ([a-zA-Z0-9_]) immediately preceding the
cursor are called the "stem".  A "completion" is any full word
that begins with the stem.  Conceptually, all possible
completions are built into a list as follows:

	look backward in the buffer from the stem location,
	and append each completion not seen before

	similarly, look forward in the buffer from the stem
	location for other new completions

	for all other open windows, look forward from the start
	of their buffers for other new completions

CompleteWord then moves forward through this list, and
CompleteWordBack moves backward through it.  If you invoke
CompleteWord when you're already at the end of the list,
a msg pops up telling you so; likewise if you're at the start
of the list and invoke CompleteWordBack.

Each time you move to a new list entry, the stem is completed
and the insertion point is moved to the end of the completion.

Example:

   	Py_BEGIN_ALLOW_THREADS
	errno = 0;
	res = fflush(f->f_fp);
	Py_END_ALLOW_THREADS
	if (res != 0) {
		PyErr_SetFromErrno(PyExc_IOError);
		clearerr(f->f_fp);
		return NULL;
	}
	Py_INCREF(Py_None);
	Py_
    Py_SomethingElse();

Suppose the cursor follows the "Py_" on the penultimate line.
Then CompleteWord will first suggest Py_None, then Py_INCREF,
then Py_END_ALLOW_THREADS, then Py_BEGIN_ALLOW_THREADS, then
Py_SomethingElse, and if you're still unhappy <wink> will go on
to search other windows' buffers.

Notes:

+ This has nothing to do with Source Insight's notion of "symbols".
  To the contrary, the long hairy words I need to type most often
  over & over are local words that SI's Complete_Symbol doesn't
  know about.  It's also the case that the word you most often
  need to type is one you most recently typed, so the macros
  work hard to suggest the closest preceding matches first.
  This flavor of completion also works fine in file types SI knows
  nothing about.

+ The list isn't actually built up at once -- as far as possible,
  it's built incrementally as you continue to invoke CompleteWord.

+ It would help if SI's SearchInBuf could search backwards.  As
  is, finding the first suggestion is done by searching the entire
  buffer forward up until the stem location, and fiddling the
  results to act "as if" things were found in the other order.
  This is clumsy, but worse if you're near the end of a long file
  with many stem matches it can take appreciable time to find the
  "first" match (since it's actually found last ...)..

+ Would be nice to be able to display msgs on the status line;
  e.g., the macros keep track of the file names and line numbers
  at which completions were found, and that's sometimes useful
  info to know (the completion process sometimes turns up
  surprises! then you'd like to know where they came from).
  The list is built into a buffer named "*Completion*", and you
  may want to peek at that.
-------------------------------------------------------------- */

macro CompleteWord()
{
	CW_guts(1)
}

macro CompleteWordBack()
{
	CW_guts(0)
}

/* BUG ALERT:  there's apparently an undocumented limit on
   string & record vrbl size (about 2**8 chars).  Makes the
   following more convoluted than I'd like, and it will still
   fail in bizarre ways if e.g the stem is "too big".
 */

/* --------------------------------------------------------------
Structure of *Completion* buffer:
	First record summarizes our state:
		.orighbuf	original buffer
		.orighwnd	original window
		.origlno	original line number
		.origi		slice indices of start ...
		.origj		... and end of stem
		.stem		original stem word
		.newj		where we left insertion point
		.index		index into *Completion* of current completion
		.searchwnd	window we're searching now
		.searchlno	next line number to search at
		.searchich  next char index to search at
	Remaining records detail unique completions:
		.file		name of file match found in
		.line		line number within file of match
		.match		the completion
-------------------------------------------------------------- */

/* Selection format
lnFirst		the first line number
ichFirst	the index of the first character on the line lnFirst
lnLast		the last line number
ichLim		the limit index (one past the last) of the last character
			on the line given in lnLast
fExtended	TRUE if the selection is extended to include more than one
            character
        .   FALSE if the selection is a simple insertion point.
Note: this is the same as the following expression:
(sel.fRect || sel.lnFirst != sel.lnLast || sel.ichFirst != sel.ichLim)

fRect		TRUE if selection is rectangular (block style),
FALSE 		if the selection is a linear range of characters.
The following fields only apply if fRect is TRUE:
xLeft		the left pixel position of the rectangle in window coordinates.
xRight		the pixel position of the right edge of the rectangle in
			window coordinates.
*/

/* Completion "word" was found in file "fname" at line "lno".
 * Search hBuf for an exact previous match to "word".  If
 * none found, append a match record to hBuf, and return
 * the match record.  If found and bReplace is false, leave
 * hBuf alone and return "".  Else replace the file & line
 * fields of the matching record, move it to end of the
 * buffer, & return it.
 */
macro CW_addword(word, fname, lno, hBuf, bReplace)
{
    /* SearchInBuf (hbuf, pattern, lnStart, ichStart,
                    fMatchCase, fRegExpr, fWholeWordsOnly)
    */
	foundit = SearchInBuf(hBuf, ";match=\"@word@\"", 1, 0, 1, 0, 0)
	record = ""
	if (foundit == "") {
		record = "file=\"@fname@\";line=\"@lno@\";match=\"@word@\""
	}
	else if (bReplace) {
		record = GetBufLine(hBuf, foundit.lnFirst)
		record.file = fname
		record.line = lno
		DelBufLine(hBuf, foundit.lnFirst)
	}
	if (record != "") {
		AppendBufLine(hBuf, record)
	}
	return record
}

/* Search in hSourceBuf for unique full-word matches to regexp,
 * up through line lastlno, adding match records to hResultBuf.
 * In the end, the match recrods look "as if" we had really
 * searched backward from lastlno, which the closest preceding
 * matches earliest in the list.
 */
macro CW_addallbackwards(regexp, hSourceBuf, hResultBuf, lastlno)
{
	lno = 0
	ich = 0	
	fname = GetBufName(hSourceBuf)
	while (1) {
	    /* SearchInBuf(hbuf, pattern, lnStart, ichStart,
	                   fMatchCase, fRegExpr, fWholeWordsOnly)
	    */
		foundit = SearchInBuf(hSourceBuf, regexp, lno, ich, 1, 1, 1)
		if (foundit == "") {
			break
		}
		lno = foundit.lnFirst
		if (lno > lastlno) {
			break
		}
		ich = foundit.ichLim
		matchline = GetBufLine(hSourceBuf, lno)
		match = strmid(matchline, foundit.ichFirst, ich)
		/* We're forced to search forward, but want the last match
		 * (closest preceding the target), so tell CW_addword to
		 * replace any previous match.
		 */
		CW_addword(match, fname, lno, hResultBuf, 1)
	}
	/* reverse the match order */
	n = GetBufLineCount(hResultBuf) - 1
	i = 1
	while (i < n) {
		r1 = GetBufLine(hResultBuf, i)
		r2 = GetBufLine(hResultBuf, n)
		PutBufLine(hResultBuf, i, r2)
		PutBufLine(hResultBuf, n, r1)
		i = i + 1
		n = n - 1
	}
}

/* The major complication here is that this is essentially an asynch
 * event-driven process:  we don't know what the user has done
 * between invocations, so have to squirrel away and check a lot
 * of state in order to guess whether they're invoking the
 * CompleteWord macros repeatedly.
 */
macro CW_guts(bForward)
{
	hwnd = GetCurrentWnd()
	selection = GetWndSel(hwnd)
	if (selection.fExtended) {
		Msg("Cannot word-complete with active selection")
		stop
	}
	hbuf = GetCurrentBuf()
	hResultBuf = GetOrCreateBuf("*Completion*")

	/* Guess whether we're continuing an old one. */
	newone = 0
	if (GetBufLineCount(hResultBuf) == 0) {
		newone = 1
	}
	else {
		stat = GetBufLine(hResultBuf, 0)
		newone = stat.orighbuf != hbuf ||
				 stat.orighwnd != hwnd ||
				 stat.origlno != selection.lnFirst ||
		         stat.newj != selection.ichFirst
	}

	/* suck up stem word */
	if (newone) {
		j = selection.ichFirst	/* index of char to right of cursor */
	}
	else {
		j = stat.origj
	}
	line = GetBufLine(hbuf, selection.lnFirst)
	i = j - 1				/* index of char to left of cursor */
	while (i >= 0) {
		ch = line[i]
		if (isupper(ch) || islower(ch) || IsNumber(ch) || ch == "_") {
			i = i - 1
		}
		else {
			break
		}
	}
	i = i + 1
	if (i >= j) {
		Msg("Cursor must follow [a-zA-Z0-9_]")
		stop
	}
	/* BUG contra docs, line[j] is not included in the following */
	word = strmid(line, i, j)
	regexp = "@word@[a-zA-Z0-9_]+"


	/* BUG "||" apparently doesn't short-circuit, so
        	if (newone || word != stat.stem)
       doesn't work (if newone, stat isn't defined)
    */
    if (!newone) {
    	/* despite that everything looks the same, they
    	   may have changed the stem! */
    	newone = word != stat.stem
    }
    if (newone) {
		stat = ""
		stat.orighbuf = hbuf
		stat.orighwnd = hwnd
		stat.origlno  = selection.lnFirst
		stat.origi    = i
		stat.origj    = j
		stat.stem     = word
		stat.newj     = j
		stat.index    = 0
		stat.searchwnd = hwnd
		stat.searchlno = selection.lnFirst
		stat.searchich = j
		ClearBuf(hResultBuf)
		AppendBufLine(hResultBuf, stat)
		CW_addallbackwards(regexp, hbuf, hResultBuf, stat.origlno)
		if (GetBufLineCount(hResultBuf) >= 2) {
			/* found at least one completion in this buffer,
			   so display the first */
			CW_completeindex(hResultBuf, 1)
			return
		}
	}

	/* continuing an old one, or a new one w/o backward match */
	n = GetBufLineCount(hResultBuf)
	i = stat.index
	if (!bForward) {
		if (i > 1) {
			CW_completeindex(hResultBuf, i - 1)
		}
		else {
			CW_completeword(hResultBuf, word, 0)
			Msg("move forward for completions")
		}
		return
	}

	/* moving forward */
	if (i < n-1) {
		CW_completeindex(hResultBuf, i + 1)
		return
	}

	if (i == n) {
		Msg("move back for completions")
		return
	}

	/* i == n-1: we're at the last one; look for another completion */
	while (1) {
		stat = GetBufLine(hResultBuf, 0)
		hwnd = stat.searchwnd
		lno	= stat.searchlno
		ich = stat.searchich
		hbuf = GetWndBuf(hwnd)
	    /* SearchInBuf(hbuf, pattern, lnStart, ichStart,
	                   fMatchCase, fRegExpr, fWholeWordsOnly)
	    */
	    if (hBuf == hResultBuf) {
	    	/* no point searching our own result list! */
	    	foundit = ""
	    }
	    else {
			foundit = SearchInBuf(hbuf, regexp, lno, ich, 1, 1, 1)
		}
		if (foundit == "") {
			hwnd = GetNextWnd(hwnd)
			if (hwnd == 0 || hwnd == stat.orighwnd) {
				n = GetBufLineCount(hResultBuf)
				if (n == 1) {
					Msg("No completions for @word@")
				}
				else {
					CW_completeword(hResultBuf, word, n)
					Msg("No more completions for @word@")
				}
				break
			}
			stat.searchwnd = hwnd
			stat.searchlno = 0
			stat.searchich = 0
			PutBufLine(hResultBuf, 0, stat)
			continue
		}
		lno = foundit.lnFirst
		ich = foundit.ichLim
		stat.searchlno = lno
		stat.searchich = ich
		PutBufLine(hResultBuf, 0, stat)
		matchline = GetBufLine(hbuf, lno)
		match = strmid(matchline, foundit.ichFirst, ich)
		result = CW_addword(match, GetBufName(hbuf), lno, hResultBuf, 0)
		if (result != "") {
			CW_completeindex(hResultBuf, GetBufLineCount(hResultBuf) - 1)
			break
		}
	}
}

/* Replace the stem with the completion at index i */
macro CW_completeindex(hBuf, i)
{
	record = GetBufLine(hBuf, i)
	CW_completeword(hBuf, record.match, i)
}

/* Replace the stem with the given completion */
macro CW_completeword(hBuf, completion, i)
{
	stat = GetBufLine(hBuf, 0)
	targetBuf = stat.orighbuf
	oldline = GetBufLine(targetBuf, stat.origlno)
	newline = cat(strmid(oldline, 0, stat.origi), completion)
	newj = strlen(newline)
	newline = cat(newline, strmid(oldline, stat.newj, strlen(oldline)))
	PutBufLine(targetBuf, stat.origlno, newline)
	SetBufIns(targetBuf, stat.origlno, newj)
	stat.newj = newj
	stat.index = i
	PutBufLine(hBuf, 0, stat)
}

/* Get handle of buffer with name "name", or create a new one
 * if no such buffer exists.
 */
macro GetOrCreateBuf(name)
{
	hBuf = GetBufHandle(name)
	if (hBuf == 0) {
		hBuf = NewBuf(name)
	}
	return hBuf
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -